compiler: Check branch and loop emission
39a13d89431d59953844c9b5a94d8a7634bf7e87f77be58d1a0799d647e8a667
1 parent
f9a33a93
lib/std/lang/lower.rad
+40 -32
| 2299 | 2299 | } |
|
| 2300 | 2300 | block.instrs.append(*instr, allocator); |
|
| 2301 | 2301 | } |
|
| 2302 | 2302 | ||
| 2303 | 2303 | /// Emit an unconditional jump to `target`. |
|
| 2304 | - | unsafe fn emitJmp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2305 | - | emit(self, il::Instr::Jmp { target: *target, args: &mut [] }); |
|
| 2304 | + | fn emitJmp 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2305 | + | unsafe { |
|
| 2306 | + | emit(self, il::Instr::Jmp { target: *target, args: &mut [] }); |
|
| 2307 | + | } |
|
| 2306 | 2308 | addPredecessor(self, target, currentBlock(self)); |
|
| 2307 | 2309 | } |
|
| 2308 | 2310 | ||
| 2309 | 2311 | /// Emit an unconditional jump to `target` with a single argument. |
|
| 2310 | - | unsafe fn emitJmpWithArg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId, arg: il::Val) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2311 | - | let args = try allocVal(self, arg); |
|
| 2312 | - | emit(self, il::Instr::Jmp { target: *target, args }); |
|
| 2312 | + | fn emitJmpWithArg 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId, arg: il::Val) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2313 | + | unsafe { |
|
| 2314 | + | let args = try allocVal(self, arg); |
|
| 2315 | + | emit(self, il::Instr::Jmp { target: *target, args }); |
|
| 2316 | + | } |
|
| 2313 | 2317 | addPredecessor(self, target, currentBlock(self)); |
|
| 2314 | 2318 | } |
|
| 2315 | 2319 | ||
| 2316 | 2320 | /// Emit an unconditional jump to `target` and switch to it. |
|
| 2317 | - | unsafe fn switchAndJumpTo 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2321 | + | fn switchAndJumpTo 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2318 | 2322 | try emitJmp(self, target); |
|
| 2319 | 2323 | switchToBlock(self, target); |
|
| 2320 | 2324 | } |
|
| 2321 | 2325 | ||
| 2322 | 2326 | /// Emit a conditional branch based on `cond`. |
|
| 2323 | - | unsafe fn emitBr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, cond: il::Reg, thenBlock: BlockId, elseBlock: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2327 | + | fn emitBr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, cond: il::Reg, thenBlock: BlockId, elseBlock: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2324 | 2328 | assert thenBlock <> elseBlock; |
|
| 2325 | - | emit(self, il::Instr::Br { |
|
| 2326 | - | op: il::CmpOp::Ne, |
|
| 2327 | - | typ: il::Type::W32, |
|
| 2328 | - | a: il::Val::Reg(cond), |
|
| 2329 | - | b: il::Val::Imm(0), |
|
| 2330 | - | thenTarget: *thenBlock, |
|
| 2331 | - | thenArgs: &mut [], |
|
| 2332 | - | elseTarget: *elseBlock, |
|
| 2333 | - | elseArgs: &mut [], |
|
| 2334 | - | }); |
|
| 2329 | + | unsafe { |
|
| 2330 | + | emit(self, il::Instr::Br { |
|
| 2331 | + | op: il::CmpOp::Ne, |
|
| 2332 | + | typ: il::Type::W32, |
|
| 2333 | + | a: il::Val::Reg(cond), |
|
| 2334 | + | b: il::Val::Imm(0), |
|
| 2335 | + | thenTarget: *thenBlock, |
|
| 2336 | + | thenArgs: &mut [], |
|
| 2337 | + | elseTarget: *elseBlock, |
|
| 2338 | + | elseArgs: &mut [], |
|
| 2339 | + | }); |
|
| 2340 | + | } |
|
| 2335 | 2341 | addPredecessor(self, thenBlock, currentBlock(self)); |
|
| 2336 | 2342 | addPredecessor(self, elseBlock, currentBlock(self)); |
|
| 2337 | 2343 | } |
|
| 2338 | 2344 | ||
| 2339 | 2345 | /// Emit a compare-and-branch instruction with the given comparison op. |
|
| 2340 | - | unsafe fn emitBrCmp 'arena 'phase 'function ( |
|
| 2346 | + | fn emitBrCmp 'arena 'phase 'function ( |
|
| 2341 | 2347 | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2342 | 2348 | op: il::CmpOp, |
|
| 2343 | 2349 | typ: il::Type, |
|
| 2344 | 2350 | a: il::Val, |
|
| 2345 | 2351 | b: il::Val, |
|
| 2346 | 2352 | thenBlock: BlockId, |
|
| 2347 | 2353 | elseBlock: BlockId |
|
| 2348 | 2354 | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2349 | 2355 | assert thenBlock <> elseBlock; |
|
| 2350 | - | emit(self, il::Instr::Br { |
|
| 2351 | - | op, typ, a, b, |
|
| 2352 | - | thenTarget: *thenBlock, thenArgs: &mut [], |
|
| 2353 | - | elseTarget: *elseBlock, elseArgs: &mut [], |
|
| 2354 | - | }); |
|
| 2356 | + | unsafe { |
|
| 2357 | + | emit(self, il::Instr::Br { |
|
| 2358 | + | op, typ, a, b, |
|
| 2359 | + | thenTarget: *thenBlock, thenArgs: &mut [], |
|
| 2360 | + | elseTarget: *elseBlock, elseArgs: &mut [], |
|
| 2361 | + | }); |
|
| 2362 | + | } |
|
| 2355 | 2363 | addPredecessor(self, thenBlock, currentBlock(self)); |
|
| 2356 | 2364 | addPredecessor(self, elseBlock, currentBlock(self)); |
|
| 2357 | 2365 | } |
|
| 2358 | 2366 | ||
| 2359 | 2367 | /// Emit a guard that traps with `ebreak` when a comparison is false. |
|
| 2360 | - | unsafe fn emitTrapUnlessCmp 'arena 'phase 'function ( |
|
| 2368 | + | fn emitTrapUnlessCmp 'arena 'phase 'function ( |
|
| 2361 | 2369 | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2362 | 2370 | op: il::CmpOp, |
|
| 2363 | 2371 | typ: il::Type, |
|
| 2364 | 2372 | a: il::Val, |
|
| 2365 | 2373 | b: il::Val |
| 2395 | 2403 | } |
|
| 2396 | 2404 | return aa <= bb; |
|
| 2397 | 2405 | } |
|
| 2398 | 2406 | ||
| 2399 | 2407 | /// Emit an `ebreak` when `a < b` holds. |
|
| 2400 | - | unsafe fn emitTrapIfLt 'arena 'phase 'function ( |
|
| 2408 | + | fn emitTrapIfLt 'arena 'phase 'function ( |
|
| 2401 | 2409 | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2402 | 2410 | typ: il::Type, |
|
| 2403 | 2411 | a: il::Val, |
|
| 2404 | 2412 | b: il::Val |
|
| 2405 | 2413 | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
| 2704 | 2712 | ||
| 2705 | 2713 | /// Emit a match binding pattern. |
|
| 2706 | 2714 | /// Binding patterns always match for regular values, but for optionals they |
|
| 2707 | 2715 | /// check for the presence of a value. Jumps to `valuePresent` on success, |
|
| 2708 | 2716 | /// `valueAbsent` on failure. |
|
| 2709 | - | unsafe fn emitBindingTest 'arena 'phase 'function ( |
|
| 2717 | + | fn emitBindingTest 'arena 'phase 'function ( |
|
| 2710 | 2718 | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 2711 | 2719 | subject: &MatchSubject, |
|
| 2712 | 2720 | valuePresent: BlockId, |
|
| 2713 | 2721 | valueAbsent: BlockId |
|
| 2714 | 2722 | ) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
| 2723 | 2731 | } |
|
| 2724 | 2732 | } |
|
| 2725 | 2733 | } |
|
| 2726 | 2734 | ||
| 2727 | 2735 | /// Emit a jump to target if the current block hasn't terminated, then seal the target block. |
|
| 2728 | - | unsafe fn emitJmpAndSeal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2736 | + | fn emitJmpAndSeal 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, target: BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2729 | 2737 | if not blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 2730 | 2738 | try emitJmp(self, target); |
|
| 2731 | 2739 | } |
|
| 2732 | 2740 | try sealBlock(self, target); |
|
| 2733 | 2741 | } |
| 2762 | 2770 | /// return 0; // @else diverges, no jump to merge. |
|
| 2763 | 2771 | /// } |
|
| 2764 | 2772 | /// |
|
| 2765 | 2773 | /// In the above example, the merge block stays `nil`, and no code is generated |
|
| 2766 | 2774 | /// after the `if`. The merge block is created on first use. |
|
| 2767 | - | unsafe fn emitMergeIfUnterminated 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, mergeBlock: &mut ?BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2775 | + | fn emitMergeIfUnterminated 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, mergeBlock: &mut ?BlockId) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 2768 | 2776 | if not blockHasTerminator(&self.blockData[*currentBlock(self)].instrs[..]) { |
|
| 2769 | 2777 | if *mergeBlock == nil { |
|
| 2770 | 2778 | set *mergeBlock = try createBlock(self, "merge"); |
|
| 2771 | 2779 | } |
|
| 2772 | 2780 | let target = *mergeBlock else { throw LowerError::MissingTarget; }; |
| 5849 | 5857 | try switchToAndSeal(self, endBlock); |
|
| 5850 | 5858 | exitLoop(self); |
|
| 5851 | 5859 | } |
|
| 5852 | 5860 | ||
| 5853 | 5861 | /// Emit an increment of a variable by `1`. |
|
| 5854 | - | unsafe fn emitIncrement 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, v: Var, typ: il::Type) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5862 | + | fn emitIncrement 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, v: Var, typ: il::Type) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 5855 | 5863 | let cur = try useVar(self, v); |
|
| 5856 | 5864 | let next = nextReg(self); |
|
| 5857 | 5865 | ||
| 5858 | 5866 | emit(self, il::Instr::BinOp { op: il::BinOp::Add, typ, dst: next, a: cur, b: il::Val::Imm(1) }); |
|
| 5859 | 5867 | defVar(self, v, il::Val::Reg(next)); |
| 5994 | 6002 | } |
|
| 5995 | 6003 | exitVarScope(&mut self.vars, savedVarsLen); |
|
| 5996 | 6004 | } |
|
| 5997 | 6005 | ||
| 5998 | 6006 | /// Lower a break statement. |
|
| 5999 | - | unsafe fn lowerBreak 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6007 | + | fn lowerBreak 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6000 | 6008 | let ctx = currentLoop(self) else { |
|
| 6001 | 6009 | throw LowerError::OutsideOfLoop; |
|
| 6002 | 6010 | }; |
|
| 6003 | 6011 | try emitJmp(self, ctx.breakTarget); |
|
| 6004 | 6012 | } |
|
| 6005 | 6013 | ||
| 6006 | 6014 | /// Lower a continue statement. |
|
| 6007 | - | unsafe fn lowerContinue 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6015 | + | fn lowerContinue 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function) throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 6008 | 6016 | let block = try getOrCreateContinueBlock(self); |
|
| 6009 | 6017 | try emitJmp(self, block); |
|
| 6010 | 6018 | } |
|
| 6011 | 6019 | ||
| 6012 | 6020 | /// Emit a return, blitting into the caller's return buffer if needed. |
| 6768 | 6776 | /// Emit a byte-copy loop: `for i in 0..size { dst[i] = src[i]; }`. |
|
| 6769 | 6777 | /// |
|
| 6770 | 6778 | /// Used when `blit` cannot be used because the copy size is dynamic. |
|
| 6771 | 6779 | /// Terminates the current block and leaves the builder positioned |
|
| 6772 | 6780 | /// after the loop. |
|
| 6773 | - | unsafe fn emitByteCopyLoop 'arena 'phase 'function ( |
|
| 6781 | + | fn emitByteCopyLoop 'arena 'phase 'function ( |
|
| 6774 | 6782 | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 6775 | 6783 | dst: il::Reg, |
|
| 6776 | 6784 | src: il::Reg, |
|
| 6777 | 6785 | size: il::Val, |
|
| 6778 | 6786 | label: *[u8] |