Optimize unconditional jumps

4579515f4565e20eb80f4f54800889b9ac1ea0a8c464cf40c23ca225f16447ed
Don't waste a slot for them.
Alexis Sellier committed ago 1 parent e5645fc3
lib/std/arch/rv64/emit.rad +13 -20
247 247
    e.funcs[e.funcsLen] = types::FuncAddr { name, index: e.codeLen };
248 248
    e.funcsLen += 1;
249 249
}
250 250
251 251
/// Record a local branch needing later patching.
252 -
/// Emits two placeholder instructions that will be patched later.
252 +
/// Unconditional jumps use a single slot (J-type, +-1MB range).
253 +
/// Conditional branches use two slots (B-type has only +-4KB range,
254 +
/// so large functions may need the inverted-branch + JAL fallback).
253 255
pub fn recordBranch(e: *mut Emitter, targetBlock: u32, kind: BranchKind) {
254 256
    if e.pendingBranchesLen >= e.pendingBranches.len {
255 257
        panic "recordBranch: buffer full";
256 258
    }
257 259
    e.pendingBranches[e.pendingBranchesLen] = PendingBranch {
259 261
        target: targetBlock,
260 262
        kind: kind,
261 263
    };
262 264
    e.pendingBranchesLen += 1;
263 265
264 -
    emit(e, encode::nop()); // Placeholder for branch/auipc.
265 -
    emit(e, encode::nop()); // Placeholder for nop/jal/jalr.
266 +
    emit(e, encode::nop()); // First slot, always needed.
267 +
268 +
    match kind {
269 +
        case BranchKind::Jump => {},
270 +
        else => emit(e, encode::nop()), // Second slot for conditional branches.
271 +
    }
266 272
}
267 273
268 274
/// Record a function call needing later patching.
269 275
/// Emits placeholder instructions that will be patched later.
270 276
/// Uses two slots to support long-distance calls.
311 317
        let p = e.pendingBranches[i];
312 318
        let offset = labels::branchToBlock(&e.labels, p.index, p.target, super::INSTR_SIZE);
313 319
        match p.kind {
314 320
            case BranchKind::Cond { op, rs1, rs2 } => {
315 321
                if encode::isBranchImm(offset) {
316 -
                    // Short: direct branch.
317 322
                    patch(e, p.index, encodeCondBranch(op, rs1, rs2, offset));
318 323
                    patch(e, p.index + 1, encode::nop());
319 324
                } else {
320 -
                    // Long: inverted branch skips `jal`; `jal` goes to target.
321 325
                    let adj = offset - super::INSTR_SIZE;
322 -
                    if not encode::isJumpImm(adj) {
323 -
                        panic "patchLocalBranches: branch offset too large";
324 -
                    }
325 326
                    patch(e, p.index, encodeInvertedBranch(op, rs1, rs2, super::INSTR_SIZE * 2));
326 327
                    patch(e, p.index + 1, encode::jal(super::ZERO, adj));
327 328
                }
328 329
            },
329 330
            case BranchKind::InvertedCond { op, rs1, rs2 } => {
330 331
                if encode::isBranchImm(offset) {
331 -
                    // Short: direct inverted branch.
332 332
                    patch(e, p.index, encodeInvertedBranch(op, rs1, rs2, offset));
333 333
                    patch(e, p.index + 1, encode::nop());
334 334
                } else {
335 -
                    // Long: non-inverted branch skips `jal`, jal goes to target.
336 335
                    let adj = offset - super::INSTR_SIZE;
337 -
                    if not encode::isJumpImm(adj) {
338 -
                        panic "patchLocalBranches: branch offset too large";
339 -
                    }
340 336
                    patch(e, p.index, encodeCondBranch(op, rs1, rs2, super::INSTR_SIZE * 2));
341 337
                    patch(e, p.index + 1, encode::jal(super::ZERO, adj));
342 338
                }
343 339
            },
344 340
            case BranchKind::Jump => {
345 -
                if encode::isJumpImm(offset) {
346 -
                    patch(e, p.index, encode::jal(super::ZERO, offset));
347 -
                    patch(e, p.index + 1, encode::nop());
348 -
                } else {
349 -
                    let s = splitImm(offset);
350 -
                    patch(e, p.index, encode::auipc(super::SCRATCH1, s.hi));
351 -
                    patch(e, p.index + 1, encode::jalr(super::ZERO, super::SCRATCH1, s.lo));
341 +
                // Single-slot jump (J-type, +-1MB range).
342 +
                if not encode::isJumpImm(offset) {
343 +
                    panic "patchLocalBranches: jump offset too large";
352 344
                }
345 +
                patch(e, p.index, encode::jal(super::ZERO, offset));
353 346
            },
354 347
        }
355 348
    }
356 349
    e.pendingBranchesLen = 0;
357 350
}