compiler: Check scalar IL emission helpers
f9a33a9346323481474221d09381d498e73b66156515e051036cc2e92e01df91
1 parent
ad670606
lib/std/lang/lower.rad
+20 -20
| 2456 | 2456 | ||
| 2457 | 2457 | try emitBr(self, condReg, thenBlock, elseBlock); |
|
| 2458 | 2458 | } |
|
| 2459 | 2459 | ||
| 2460 | 2460 | /// Emit a 32-bit store instruction at the given offset. |
|
| 2461 | - | unsafe fn emitStoreW32At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Val, dst: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2461 | + | fn emitStoreW32At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Val, dst: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2462 | 2462 | emit(self, il::Instr::Store { typ: il::Type::W32, src, dst, offset }); |
|
| 2463 | 2463 | } |
|
| 2464 | 2464 | ||
| 2465 | 2465 | /// Emit a 32-bit load instruction at the given offset. |
|
| 2466 | - | unsafe fn emitLoadW32At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, dst: il::Reg, src: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2466 | + | fn emitLoadW32At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, dst: il::Reg, src: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2467 | 2467 | emit(self, il::Instr::Load { typ: il::Type::W32, dst, src, offset }); |
|
| 2468 | 2468 | } |
|
| 2469 | 2469 | ||
| 2470 | 2470 | /// Emit an 8-bit store instruction at the given offset. |
|
| 2471 | - | unsafe fn emitStoreW8At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Val, dst: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2471 | + | fn emitStoreW8At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Val, dst: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2472 | 2472 | emit(self, il::Instr::Store { typ: il::Type::W8, src, dst, offset }); |
|
| 2473 | 2473 | } |
|
| 2474 | 2474 | ||
| 2475 | 2475 | /// Emit an 8-bit load instruction at the given offset. |
|
| 2476 | - | unsafe fn emitLoadW8At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, dst: il::Reg, src: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2476 | + | fn emitLoadW8At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, dst: il::Reg, src: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2477 | 2477 | emit(self, il::Instr::Load { typ: il::Type::W8, dst, src, offset }); |
|
| 2478 | 2478 | } |
|
| 2479 | 2479 | ||
| 2480 | 2480 | /// Emit a 64-bit store instruction at the given offset. |
|
| 2481 | - | unsafe fn emitStoreW64At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Val, dst: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2481 | + | fn emitStoreW64At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Val, dst: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2482 | 2482 | emit(self, il::Instr::Store { typ: il::Type::W64, src, dst, offset }); |
|
| 2483 | 2483 | } |
|
| 2484 | 2484 | ||
| 2485 | 2485 | /// Emit a 64-bit load instruction at the given offset. |
|
| 2486 | - | unsafe fn emitLoadW64At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, dst: il::Reg, src: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2486 | + | fn emitLoadW64At 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, dst: il::Reg, src: il::Reg, offset: i32) where 'arena: 'phase, 'phase: 'function { |
|
| 2487 | 2487 | emit(self, il::Instr::Load { typ: il::Type::W64, dst, src, offset }); |
|
| 2488 | 2488 | } |
|
| 2489 | 2489 | ||
| 2490 | 2490 | /// Load a tag from memory at `src` plus `offset` with the given IL type. |
|
| 2491 | - | unsafe fn loadTag 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Reg, offset: i32, tagType: il::Type) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 2491 | + | fn loadTag 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, src: il::Reg, offset: i32, tagType: il::Type) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 2492 | 2492 | let dst = nextReg(self); |
|
| 2493 | 2493 | emit(self, il::Instr::Load { typ: tagType, dst, src, offset }); |
|
| 2494 | 2494 | return il::Val::Reg(dst); |
|
| 2495 | 2495 | } |
|
| 2496 | 2496 | ||
| 2497 | 2497 | /// Load the data pointer from a slice value. |
|
| 2498 | - | unsafe fn loadSlicePtr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sliceReg: il::Reg) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 2498 | + | fn loadSlicePtr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sliceReg: il::Reg) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 2499 | 2499 | let ptrReg = nextReg(self); |
|
| 2500 | 2500 | emitLoadW64At(self, ptrReg, sliceReg, SLICE_PTR_OFFSET); |
|
| 2501 | 2501 | return ptrReg; |
|
| 2502 | 2502 | } |
|
| 2503 | 2503 | ||
| 2504 | 2504 | /// Load the length from a slice value. |
|
| 2505 | - | unsafe fn loadSliceLen 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sliceReg: il::Reg) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 2505 | + | fn loadSliceLen 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sliceReg: il::Reg) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 2506 | 2506 | let lenReg = nextReg(self); |
|
| 2507 | 2507 | emitLoadW32At(self, lenReg, sliceReg, SLICE_LEN_OFFSET); |
|
| 2508 | 2508 | return il::Val::Reg(lenReg); |
|
| 2509 | 2509 | } |
|
| 2510 | 2510 | ||
| 2511 | 2511 | /// Return a fixed array length or load the length from a slice value. |
|
| 2512 | - | unsafe fn containerLength 'arena 'phase 'function ( |
|
| 2512 | + | fn containerLength 'arena 'phase 'function ( |
|
| 2513 | 2513 | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2514 | 2514 | base: il::Reg, |
|
| 2515 | 2515 | length: ?u32 |
|
| 2516 | 2516 | ) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 2517 | 2517 | if let len = length { // Array (length is known). |
| 2520 | 2520 | // Slice (length must be loaded). |
|
| 2521 | 2521 | return loadSliceLen(self, base); |
|
| 2522 | 2522 | } |
|
| 2523 | 2523 | ||
| 2524 | 2524 | /// Load the capacity from a slice value. |
|
| 2525 | - | unsafe fn loadSliceCap 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sliceReg: il::Reg) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 2525 | + | fn loadSliceCap 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, sliceReg: il::Reg) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 2526 | 2526 | let capReg = nextReg(self); |
|
| 2527 | 2527 | emitLoadW32At(self, capReg, sliceReg, SLICE_CAP_OFFSET); |
|
| 2528 | 2528 | return il::Val::Reg(capReg); |
|
| 2529 | 2529 | } |
|
| 2530 | 2530 |
| 3406 | 3406 | else => return false, |
|
| 3407 | 3407 | } |
|
| 3408 | 3408 | } |
|
| 3409 | 3409 | ||
| 3410 | 3410 | /// Load the tag byte from a tagged value aggregate (optionals and unions). |
|
| 3411 | - | unsafe fn tvalTagReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 3411 | + | fn tvalTagReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 3412 | 3412 | let tagReg = nextReg(self); |
|
| 3413 | 3413 | emitLoadW8At(self, tagReg, base, TVAL_TAG_OFFSET); |
|
| 3414 | 3414 | return tagReg; |
|
| 3415 | 3415 | } |
|
| 3416 | 3416 | ||
| 3417 | 3417 | /// Load the tag word from a result aggregate. |
|
| 3418 | - | unsafe fn resultTagReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 3418 | + | fn resultTagReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 3419 | 3419 | let tagReg = nextReg(self); |
|
| 3420 | 3420 | emitLoadW64At(self, tagReg, base, TVAL_TAG_OFFSET); |
|
| 3421 | 3421 | return tagReg; |
|
| 3422 | 3422 | } |
|
| 3423 | 3423 | ||
| 3424 | 3424 | /// Get the register to compare against `0` for optional `nil` checking. |
|
| 3425 | 3425 | /// For null-ptr-optimized types, loads the data pointer, or returns it |
|
| 3426 | 3426 | /// directly for scalar pointers. For aggregates, returns the tag register. |
|
| 3427 | - | unsafe fn optionalNilReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3427 | + | fn optionalNilReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3428 | 3428 | let reg = emitValToReg(self, val); |
|
| 3429 | 3429 | ||
| 3430 | 3430 | match typ { |
|
| 3431 | 3431 | case resolver::Type::Optional(resolver::Type::Slice { .. }) => { |
|
| 3432 | 3432 | let ptrReg = nextReg(self); |
| 4425 | 4425 | let layout = resolver::getTypeLayout(typ); |
|
| 4426 | 4426 | return emitReserveLayout(self, layout); |
|
| 4427 | 4427 | } |
|
| 4428 | 4428 | ||
| 4429 | 4429 | /// Reserve stack storage with an explicit layout. |
|
| 4430 | - | unsafe fn emitReserveLayout 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, layout: resolver::Layout) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 4430 | + | fn emitReserveLayout 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, layout: resolver::Layout) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 4431 | 4431 | let dst = nextReg(self); |
|
| 4432 | 4432 | ||
| 4433 | 4433 | emit(self, il::Instr::Reserve { |
|
| 4434 | 4434 | dst, |
|
| 4435 | 4435 | size: il::Val::Imm(layout.size as i64), |
| 4611 | 4611 | }); |
|
| 4612 | 4612 | return il::Val::Reg(slot); |
|
| 4613 | 4613 | } |
|
| 4614 | 4614 | ||
| 4615 | 4615 | /// Compute a field pointer by adding a byte offset to a base address. |
|
| 4616 | - | unsafe fn emitPtrOffset 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg, offset: i32) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 4616 | + | fn emitPtrOffset 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg, offset: i32) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 4617 | 4617 | if offset == 0 { |
|
| 4618 | 4618 | return base; |
|
| 4619 | 4619 | } |
|
| 4620 | 4620 | let dst = nextReg(self); |
|
| 4621 | 4621 |
| 4629 | 4629 | return dst; |
|
| 4630 | 4630 | } |
|
| 4631 | 4631 | ||
| 4632 | 4632 | /// Emit an element address computation for array/slice indexing. |
|
| 4633 | 4633 | /// Computes: `base + idx * stride`. |
|
| 4634 | - | unsafe fn emitElem 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, stride: u32, base: il::Reg, idx: il::Val) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 4634 | + | fn emitElem 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, stride: u32, base: il::Reg, idx: il::Val) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 4635 | 4635 | // If index is zero, return base directly. |
|
| 4636 | 4636 | if idx == il::Val::Imm(0) { |
|
| 4637 | 4637 | return base; |
|
| 4638 | 4638 | } |
|
| 4639 | 4639 | // If stride is `1`, skip the multiply. |
| 4670 | 4670 | }); |
|
| 4671 | 4671 | return dst; |
|
| 4672 | 4672 | } |
|
| 4673 | 4673 | ||
| 4674 | 4674 | /// Emit a typed binary operation, returning the result as a value. |
|
| 4675 | - | unsafe fn emitTypedBinOp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, op: il::BinOp, typ: il::Type, a: il::Val, b: il::Val) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 4675 | + | fn emitTypedBinOp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, op: il::BinOp, typ: il::Type, a: il::Val, b: il::Val) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 4676 | 4676 | let dst = nextReg(self); |
|
| 4677 | 4677 | emit(self, il::Instr::BinOp { op, typ, dst, a, b }); |
|
| 4678 | 4678 | return il::Val::Reg(dst); |
|
| 4679 | 4679 | } |
|
| 4680 | 4680 |
| 4690 | 4690 | let binOp = il::BinOp::Eq if op == ast::BinaryOp::Eq else il::BinOp::Ne; |
|
| 4691 | 4691 | return emitTypedBinOp(self, binOp, il::Type::W8, tag, il::Val::Imm(tagIdx)); |
|
| 4692 | 4692 | } |
|
| 4693 | 4693 | ||
| 4694 | 4694 | /// Logical "and" between two values. Returns the result in a register. |
|
| 4695 | - | unsafe fn emitLogicalAnd 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, left: ?il::Val, right: il::Val) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 4695 | + | fn emitLogicalAnd 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, left: ?il::Val, right: il::Val) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 4696 | 4696 | let prev = left else { |
|
| 4697 | 4697 | return right; |
|
| 4698 | 4698 | }; |
|
| 4699 | 4699 | return emitTypedBinOp(self, il::BinOp::And, il::Type::W32, prev, right); |
|
| 4700 | 4700 | } |
| 6069 | 6069 | ||
| 6070 | 6070 | try emitRetVal(self, resultVal); |
|
| 6071 | 6071 | } |
|
| 6072 | 6072 | ||
| 6073 | 6073 | /// Ensure a value is in a register (eg. for branch conditions). |
|
| 6074 | - | unsafe fn emitValToReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 6074 | + | fn emitValToReg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, val: il::Val) -> il::Reg where 'arena: 'phase, 'phase: 'function { |
|
| 6075 | 6075 | match val { |
|
| 6076 | 6076 | case il::Val::Reg(r) => return r, |
|
| 6077 | 6077 | case il::Val::Imm(_), il::Val::DataSym(_), il::Val::FnAddr(_) => { |
|
| 6078 | 6078 | let dst = nextReg(self); |
|
| 6079 | 6079 | emit(self, il::Instr::Copy { dst, val }); |