For branch targets that are external/static symbols (where the offset cannot be determined at assembly time), expand to: b<inverse> .+8 # skip if condition false auipc t0, %pcrel_hi # load target upper bits jalr x0, %pcrel_lo(t0) # jump This handles the ±4 KiB B-type limitation without requiring linker relaxation (R_RISCV_RELAX). Local immediates within range still use the compact B-type encoding.
Unified · Split
| @@ -247,11 +247,9 @@ static void parse_branch_offset_operand(TCCState *s1, Operand *op){ · 246 unmodified lines | |||
| 247 | if ((int) op->e.v >= -0x1000 && (int) op->e.v < 0x1000) | 247 | if ((int) op->e.v >= -0x1000 && (int) op->e.v < 0x1000) |
| 248 | op->type = OP_IM12S; | 248 | op->type = OP_IM12S; |
| 249 | } else if (op->e.sym->type.t & (VT_EXTERN | VT_STATIC)) { | 249 | } else if (op->e.sym->type.t & (VT_EXTERN | VT_STATIC)) { |
| 250 | greloca(cur_text_section, op->e.sym, ind, R_RISCV_BRANCH, 0); | 250 | /* For extern/static symbols, always use far-branch expansion |
| 251 | 251 | since linker relaxation (R_RISCV_RELAX) is not implemented. */ | |
| 252 | /* XXX: Implement far branches */ | 252 | op->type = OP_IM32; |
| 253 | |||
| 254 | op->type = OP_IM12S; | ||
| 255 | op->e.v = 0; | 253 | op->e.v = 0; |
| 256 | } else { | 254 | } else { |
| 257 | expect("operand"); | 255 | expect("operand"); |
| @@ -1384,6 +1382,26 @@ static void asm_emit_b(int token, uint32_t opcode, const Operand *rs1, const Ope · 1126 unmodified lines | |||
| 1384 | if (rs2->type != OP_REG) { | 1382 | if (rs2->type != OP_REG) { |
| 1385 | tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); | 1383 | tcc_error("'%s': Expected destination operand that is a register", get_tok_str(token, NULL)); |
| 1386 | } | 1384 | } |
| 1385 | if (imm->type == OP_IM32 && imm->e.sym) { | ||
| 1386 | /* far branch: expand to inverted short branch + auipc + jalr */ | ||
| 1387 | int b_ofs = ind; | ||
| 1388 | uint32_t inv_func3 = ((opcode >> 12) & 7) ^ 1; | ||
| 1389 | uint32_t inv_opcode = (opcode & ~(7 << 12)) | (inv_func3 << 12); | ||
| 1390 | /* b<inverse> .+8 */ | ||
| 1391 | asm_emit_opcode(inv_opcode | ENCODE_RS1(rs1->reg) | ||
| 1392 | | ENCODE_RS2(rs2->reg) | (1 << 8)); | ||
| 1393 | /* auipc t0, 0 */ | ||
| 1394 | greloca(cur_text_section, imm->e.sym, ind, R_RISCV_CALL, 0); | ||
| 1395 | asm_emit_opcode(0x17 | ENCODE_RD(5)); | ||
| 1396 | /* jalr x0, 0(t0) */ | ||
| 1397 | write32le(cur_text_section->data + b_ofs, | ||
| 1398 | read32le(cur_text_section->data + b_ofs) | ||
| 1399 | | (((ind - b_ofs) >> 1) & 0xf) << 8 | ||
| 1400 | | (((ind - b_ofs) >> 5) & 0x3f) << 25 | ||
| 1401 | | (((ind - b_ofs) >> 11) & 1) << 7 | ||
| 1402 | | (((ind - b_ofs) >> 12) & 1) << 31); | ||
| 1403 | return; | ||
| 1404 | } | ||
| 1387 | if (imm->type != OP_IM12S) { | 1405 | if (imm->type != OP_IM12S) { |
| 1388 | tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 8191", get_tok_str(token, NULL)); | 1406 | tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 8191", get_tok_str(token, NULL)); |
| 1389 | } | 1407 | } |