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 | 247 | if ((int) op->e.v >= -0x1000 && (int) op->e.v < 0x1000) |
| 248 | 248 | op->type = OP_IM12S; |
| 249 | 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); | |
| 251 | − | |
| 252 | − /* XXX: Implement far branches */ | |
| 253 | − | |
| 254 | − op->type = OP_IM12S; | |
| 250 | + /* For extern/static symbols, always use far-branch expansion | |
| 251 | + since linker relaxation (R_RISCV_RELAX) is not implemented. */ | |
| 252 | + op->type = OP_IM32; | |
| 255 | 253 | op->e.v = 0; |
| 256 | 254 | } else { |
| 257 | 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 | 1382 | if (rs2->type != OP_REG) { |
| 1385 | 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 | 1405 | if (imm->type != OP_IM12S) { |
| 1388 | 1406 | tcc_error("'%s': Expected second source operand that is an immediate value between 0 and 8191", get_tok_str(token, NULL)); |
| 1389 | 1407 | } |