Use unlabeled records in more places
60a9450d88ee83d5edf2301eaad6ed76cf28b5f9ced7df3eee856333a4e48378
1 parent
e291fc36
lib/std/lang/lower.rad
+56 -63
| 374 | 374 | // a mapping from [`Var`] to current SSA value. When control flow merges, |
|
| 375 | 375 | // block parameters are inserted to merge values from different control flow paths. |
|
| 376 | 376 | ||
| 377 | 377 | /// A variable handle. Represents a source-level variable during lowering. |
|
| 378 | 378 | /// The same [`Var`] can have different SSA values in different blocks. |
|
| 379 | - | // TODO: Use `Var(u32)`. |
|
| 380 | - | pub record Var { |
|
| 381 | - | /// Index into the function's variables array. |
|
| 382 | - | id: u32, |
|
| 383 | - | } |
|
| 379 | + | pub record Var(u32); |
|
| 384 | 380 | ||
| 385 | 381 | /// Metadata for a source-level variable, stored once per function. |
|
| 386 | 382 | /// |
|
| 387 | 383 | /// Each variable declaration in the source creates one [`VarData`] entry in the |
|
| 388 | 384 | /// function's `variables` array, indexed by `id`. This contains static |
| 425 | 421 | // instructions until terminated by a jump, branch, return, or unreachable. |
|
| 426 | 422 | // Blocks can be created before they're filled (forward references for jumps). |
|
| 427 | 423 | ||
| 428 | 424 | /// A handle to a basic block within the current function. |
|
| 429 | 425 | /// Block handles are stable, they don't change as more blocks are added. |
|
| 430 | - | pub record BlockId { |
|
| 431 | - | /// Index into the function's block array. |
|
| 432 | - | n: u32, |
|
| 433 | - | } |
|
| 426 | + | pub record BlockId(u32); |
|
| 434 | 427 | ||
| 435 | 428 | /// Internal block state during construction. |
|
| 436 | 429 | /// |
|
| 437 | 430 | /// The key invariants: |
|
| 438 | 431 | /// |
| 1791 | 1784 | /// The latter is necessary because recursive SSA resolution may have already |
|
| 1792 | 1785 | /// patched terminator arguments with the provisional value before it was |
|
| 1793 | 1786 | /// found to be trivial. |
|
| 1794 | 1787 | fn rewriteCachedVarValue(self: *mut FnLowerer, v: Var, from: il::Val, to: il::Val) { |
|
| 1795 | 1788 | for i in 0..self.blockData.len { |
|
| 1796 | - | let blk = getBlockMut(self, BlockId { n: i }); |
|
| 1797 | - | if blk.vars[v.id] == from { |
|
| 1798 | - | blk.vars[v.id] = to; |
|
| 1789 | + | let blk = getBlockMut(self, BlockId(i)); |
|
| 1790 | + | if blk.vars[*v] == from { |
|
| 1791 | + | blk.vars[*v] = to; |
|
| 1799 | 1792 | } |
|
| 1800 | 1793 | if blk.instrs.len > 0 { |
|
| 1801 | 1794 | let ix = blk.instrs.len - 1; |
|
| 1802 | 1795 | match &mut blk.instrs[ix] { |
|
| 1803 | 1796 | case il::Instr::Jmp { args, .. } => |
| 1848 | 1841 | /// The block is initially unsealed (predecessors may be added later) and empty. |
|
| 1849 | 1842 | /// Returns a [`BlockId`] that can be used for jumps and branches. The block must |
|
| 1850 | 1843 | /// be switched to via [`switchToBlock`] before instructions can be emitted. |
|
| 1851 | 1844 | fn createBlock(self: *mut FnLowerer, labelBase: *[u8]) -> BlockId throws (LowerError) { |
|
| 1852 | 1845 | let label = try nextLabel(self, labelBase); |
|
| 1853 | - | let id = BlockId { n: self.blockData.len }; |
|
| 1846 | + | let id = BlockId(self.blockData.len); |
|
| 1854 | 1847 | let varCount = self.fnType.localCount; |
|
| 1855 | 1848 | let vars = try! alloc::allocSlice(self.low.arena, @sizeOf(?il::Val), @alignOf(?il::Val), varCount) as *mut [?il::Val]; |
|
| 1856 | 1849 | ||
| 1857 | 1850 | for i in 0..varCount { |
|
| 1858 | 1851 | vars[i] = nil; |
| 1903 | 1896 | }; |
|
| 1904 | 1897 | blk.sealState = Sealed::Yes; |
|
| 1905 | 1898 | ||
| 1906 | 1899 | // Complete all incomplete block parameters. |
|
| 1907 | 1900 | for varId in incompleteVars { |
|
| 1908 | - | try resolveBlockArgs(self, block, Var { id: varId }); |
|
| 1901 | + | try resolveBlockArgs(self, block, Var(varId)); |
|
| 1909 | 1902 | } |
|
| 1910 | 1903 | } |
|
| 1911 | 1904 | ||
| 1912 | 1905 | /// Seal a block and switch to it. |
|
| 1913 | 1906 | fn switchToAndSeal(self: *mut FnLowerer, block: BlockId) throws (LowerError) { |
| 1915 | 1908 | switchToBlock(self, block); |
|
| 1916 | 1909 | } |
|
| 1917 | 1910 | ||
| 1918 | 1911 | /// Get block data by block id. |
|
| 1919 | 1912 | fn getBlock(self: *FnLowerer, block: BlockId) -> *BlockData { |
|
| 1920 | - | return &self.blockData[block.n]; |
|
| 1913 | + | return &self.blockData[*block]; |
|
| 1921 | 1914 | } |
|
| 1922 | 1915 | ||
| 1923 | 1916 | /// Get mutable block data by block id. |
|
| 1924 | 1917 | fn getBlockMut(self: *mut FnLowerer, block: BlockId) -> *mut BlockData { |
|
| 1925 | - | return &mut self.blockData[block.n]; |
|
| 1918 | + | return &mut self.blockData[*block]; |
|
| 1926 | 1919 | } |
|
| 1927 | 1920 | ||
| 1928 | 1921 | /// Get the current block being built. |
|
| 1929 | 1922 | fn currentBlock(self: *FnLowerer) -> BlockId { |
|
| 1930 | 1923 | let block = self.currentBlock else { |
| 1957 | 1950 | block.instrs.append(instr, self.allocator); |
|
| 1958 | 1951 | } |
|
| 1959 | 1952 | ||
| 1960 | 1953 | /// Emit an unconditional jump to `target`. |
|
| 1961 | 1954 | fn emitJmp(self: *mut FnLowerer, target: BlockId) throws (LowerError) { |
|
| 1962 | - | emit(self, il::Instr::Jmp { target: target.n, args: &mut [] }); |
|
| 1955 | + | emit(self, il::Instr::Jmp { target: *target, args: &mut [] }); |
|
| 1963 | 1956 | addPredecessor(self, target, currentBlock(self)); |
|
| 1964 | 1957 | } |
|
| 1965 | 1958 | ||
| 1966 | 1959 | /// Emit an unconditional jump to `target` with a single argument. |
|
| 1967 | 1960 | fn emitJmpWithArg(self: *mut FnLowerer, target: BlockId, arg: il::Val) throws (LowerError) { |
|
| 1968 | 1961 | let args = try allocVal(self, arg); |
|
| 1969 | - | emit(self, il::Instr::Jmp { target: target.n, args }); |
|
| 1962 | + | emit(self, il::Instr::Jmp { target: *target, args }); |
|
| 1970 | 1963 | addPredecessor(self, target, currentBlock(self)); |
|
| 1971 | 1964 | } |
|
| 1972 | 1965 | ||
| 1973 | 1966 | /// Emit an unconditional jump to `target` and switch to it. |
|
| 1974 | 1967 | fn switchAndJumpTo(self: *mut FnLowerer, target: BlockId) throws (LowerError) { |
| 1982 | 1975 | emit(self, il::Instr::Br { |
|
| 1983 | 1976 | op: il::CmpOp::Ne, |
|
| 1984 | 1977 | typ: il::Type::W32, |
|
| 1985 | 1978 | a: il::Val::Reg(cond), |
|
| 1986 | 1979 | b: il::Val::Imm(0), |
|
| 1987 | - | thenTarget: thenBlock.n, |
|
| 1980 | + | thenTarget: *thenBlock, |
|
| 1988 | 1981 | thenArgs: &mut [], |
|
| 1989 | - | elseTarget: elseBlock.n, |
|
| 1982 | + | elseTarget: *elseBlock, |
|
| 1990 | 1983 | elseArgs: &mut [], |
|
| 1991 | 1984 | }); |
|
| 1992 | 1985 | addPredecessor(self, thenBlock, currentBlock(self)); |
|
| 1993 | 1986 | addPredecessor(self, elseBlock, currentBlock(self)); |
|
| 1994 | 1987 | } |
| 2004 | 1997 | elseBlock: BlockId |
|
| 2005 | 1998 | ) throws (LowerError) { |
|
| 2006 | 1999 | assert thenBlock != elseBlock; |
|
| 2007 | 2000 | emit(self, il::Instr::Br { |
|
| 2008 | 2001 | op, typ, a, b, |
|
| 2009 | - | thenTarget: thenBlock.n, thenArgs: &mut [], |
|
| 2010 | - | elseTarget: elseBlock.n, elseArgs: &mut [], |
|
| 2002 | + | thenTarget: *thenBlock, thenArgs: &mut [], |
|
| 2003 | + | elseTarget: *elseBlock, elseArgs: &mut [], |
|
| 2011 | 2004 | }); |
|
| 2012 | 2005 | addPredecessor(self, thenBlock, currentBlock(self)); |
|
| 2013 | 2006 | addPredecessor(self, elseBlock, currentBlock(self)); |
|
| 2014 | 2007 | } |
|
| 2015 | 2008 |
| 2380 | 2373 | fn addPredecessor(self: *mut FnLowerer, target: BlockId, pred: BlockId) { |
|
| 2381 | 2374 | let blk = getBlockMut(self, target); |
|
| 2382 | 2375 | assert blk.sealState != Sealed::Yes, "addPredecessor: adding predecessor to sealed block"; |
|
| 2383 | 2376 | let preds = &mut blk.preds; |
|
| 2384 | 2377 | for i in 0..preds.len { |
|
| 2385 | - | if preds[i] == pred.n { // Avoid duplicate predecessor entries. |
|
| 2378 | + | if preds[i] == *pred { // Avoid duplicate predecessor entries. |
|
| 2386 | 2379 | return; |
|
| 2387 | 2380 | } |
|
| 2388 | 2381 | } |
|
| 2389 | - | preds.append(pred.n, self.allocator); |
|
| 2382 | + | preds.append(*pred, self.allocator); |
|
| 2390 | 2383 | } |
|
| 2391 | 2384 | ||
| 2392 | 2385 | /// Finalize all blocks and return the block array. |
|
| 2393 | 2386 | fn finalizeBlocks(self: *mut FnLowerer) -> *[il::Block] throws (LowerError) { |
|
| 2394 | 2387 | let blocks = try! alloc::allocSlice( |
| 2562 | 2555 | val: il::Val |
|
| 2563 | 2556 | ) -> Var { |
|
| 2564 | 2557 | let id = self.vars.len; |
|
| 2565 | 2558 | self.vars.append(VarData { name, type, mutable, addressTaken: false }, self.allocator); |
|
| 2566 | 2559 | ||
| 2567 | - | let v = Var { id }; |
|
| 2560 | + | let v = Var(id); |
|
| 2568 | 2561 | if self.currentBlock != nil { |
|
| 2569 | 2562 | defVar(self, v, val); |
|
| 2570 | 2563 | } |
|
| 2571 | 2564 | return v; |
|
| 2572 | 2565 | } |
| 2574 | 2567 | /// Define (write) a variable. Record the SSA value of a variable in the |
|
| 2575 | 2568 | /// current block. Called when a variable is assigned or initialized (`let` |
|
| 2576 | 2569 | /// bindings, assignments, loop updates). When [`useVar`] is later called, |
|
| 2577 | 2570 | /// it will retrieve this value. |
|
| 2578 | 2571 | fn defVar(self: *mut FnLowerer, v: Var, val: il::Val) { |
|
| 2579 | - | assert v.id < self.vars.len; |
|
| 2580 | - | getBlockMut(self, currentBlock(self)).vars[v.id] = val; |
|
| 2572 | + | assert *v < self.vars.len; |
|
| 2573 | + | getBlockMut(self, currentBlock(self)).vars[*v] = val; |
|
| 2581 | 2574 | } |
|
| 2582 | 2575 | ||
| 2583 | 2576 | /// Use (read) the current value of a variable in the current block. |
|
| 2584 | 2577 | /// May insert block parameters if the value must come from predecessors. |
|
| 2585 | 2578 | fn useVar(self: *mut FnLowerer, v: Var) -> il::Val throws (LowerError) { |
| 2591 | 2584 | /// Given a variable and a block where it's used, this function finds the |
|
| 2592 | 2585 | /// correct [`il::Val`] that holds the variable's value at that program point. |
|
| 2593 | 2586 | /// When control flow merges from multiple predecessors with different |
|
| 2594 | 2587 | /// definitions, it creates a block parameter to unify them. |
|
| 2595 | 2588 | fn useVarInBlock(self: *mut FnLowerer, block: BlockId, v: Var) -> il::Val throws (LowerError) { |
|
| 2596 | - | assert v.id < self.vars.len; |
|
| 2589 | + | assert *v < self.vars.len; |
|
| 2597 | 2590 | ||
| 2598 | 2591 | let blk = getBlockMut(self, block); |
|
| 2599 | - | if let val = blk.vars[v.id] { |
|
| 2592 | + | if let val = blk.vars[*v] { |
|
| 2600 | 2593 | return val; |
|
| 2601 | 2594 | } |
|
| 2602 | 2595 | // Entry block cannot have block parameters. If variable isn't defined |
|
| 2603 | 2596 | // in entry, we return undefined. |
|
| 2604 | 2597 | if block == self.entryBlock { |
| 2610 | 2603 | throw LowerError::InvalidUse; |
|
| 2611 | 2604 | } |
|
| 2612 | 2605 | // Single predecessor means no merge needed, variable is implicitly |
|
| 2613 | 2606 | // available without a block parameter. |
|
| 2614 | 2607 | if blk.preds.len == 1 { |
|
| 2615 | - | let pred = BlockId { n: blk.preds[0] }; |
|
| 2616 | - | if pred.n != block.n { |
|
| 2608 | + | let pred = BlockId(blk.preds[0]); |
|
| 2609 | + | if *pred != *block { |
|
| 2617 | 2610 | let val = try useVarInBlock(self, pred, v); |
|
| 2618 | - | blk.vars[v.id] = val; // Cache. |
|
| 2611 | + | blk.vars[*v] = val; // Cache. |
|
| 2619 | 2612 | return val; |
|
| 2620 | 2613 | } |
|
| 2621 | 2614 | } |
|
| 2622 | 2615 | } |
|
| 2623 | 2616 | // Multiple predecessors or unsealed block: need a block parameter to merge |
| 2632 | 2625 | while id > 0 { |
|
| 2633 | 2626 | id -= 1; |
|
| 2634 | 2627 | if let varName = self.vars[id].name { |
|
| 2635 | 2628 | // Names are interned strings, so pointer comparison suffices. |
|
| 2636 | 2629 | if varName == name { |
|
| 2637 | - | return Var { id }; |
|
| 2630 | + | return Var(id); |
|
| 2638 | 2631 | } |
|
| 2639 | 2632 | } |
|
| 2640 | 2633 | } |
|
| 2641 | 2634 | return nil; |
|
| 2642 | 2635 | } |
| 2659 | 2652 | self.vars = @sliceOf(self.vars.ptr, savedVarsLen, self.vars.cap); |
|
| 2660 | 2653 | } |
|
| 2661 | 2654 | ||
| 2662 | 2655 | /// Get the metadata for a variable. |
|
| 2663 | 2656 | fn getVar(self: *FnLowerer, v: Var) -> *VarData { |
|
| 2664 | - | assert v.id < self.vars.len; |
|
| 2665 | - | return &self.vars[v.id]; |
|
| 2657 | + | assert *v < self.vars.len; |
|
| 2658 | + | return &self.vars[*v]; |
|
| 2666 | 2659 | } |
|
| 2667 | 2660 | ||
| 2668 | 2661 | /// Create a block parameter to merge a variable's value from multiple |
|
| 2669 | 2662 | /// control flow paths. |
|
| 2670 | 2663 | /// |
| 2690 | 2683 | ||
| 2691 | 2684 | // Create block parameter and add it to the block. |
|
| 2692 | 2685 | let param = il::Param { value: reg, type }; |
|
| 2693 | 2686 | let blk = getBlockMut(self, block); |
|
| 2694 | 2687 | blk.params.append(param, self.allocator); |
|
| 2695 | - | blk.paramVars.append(v.id, self.allocator); // Associate variable with parameter. |
|
| 2688 | + | blk.paramVars.append(*v, self.allocator); // Associate variable with parameter. |
|
| 2696 | 2689 | ||
| 2697 | 2690 | // Record that this variable's value in this block is now the parameter register. |
|
| 2698 | 2691 | // This must happen before the predecessor loop to handle self-referential loops. |
|
| 2699 | - | blk.vars[v.id] = il::Val::Reg(reg); |
|
| 2692 | + | blk.vars[*v] = il::Val::Reg(reg); |
|
| 2700 | 2693 | ||
| 2701 | 2694 | match &mut blk.sealState { |
|
| 2702 | 2695 | case Sealed::No { incompleteVars } => { |
|
| 2703 | 2696 | // Block unsealed: defer until sealing. |
|
| 2704 | - | incompleteVars.append(v.id, self.allocator); |
|
| 2697 | + | incompleteVars.append(*v, self.allocator); |
|
| 2705 | 2698 | }, |
|
| 2706 | 2699 | case Sealed::Yes => { |
|
| 2707 | 2700 | // Block sealed: check for trivial phi before committing. If all |
|
| 2708 | 2701 | // predecessors provide the same value, we can remove the param we |
|
| 2709 | 2702 | // just created and use that value directly. |
|
| 2710 | 2703 | if let trivial = try getTrivialPhiVal(self, block, v) { |
|
| 2711 | 2704 | let provisional = il::Val::Reg(reg); |
|
| 2712 | 2705 | removeLastBlockParam(self, block); |
|
| 2713 | 2706 | rewriteCachedVarValue(self, v, provisional, trivial); |
|
| 2714 | - | getBlockMut(self, block).vars[v.id] = trivial; |
|
| 2707 | + | getBlockMut(self, block).vars[*v] = trivial; |
|
| 2715 | 2708 | return trivial; |
|
| 2716 | 2709 | } |
|
| 2717 | 2710 | // Non-trivial phi: patch predecessors to pass their values. |
|
| 2718 | 2711 | try resolveBlockArgs(self, block, v); |
|
| 2719 | 2712 | }, |
| 2737 | 2730 | ||
| 2738 | 2731 | // Find the parameter index corresponding to this variable. |
|
| 2739 | 2732 | // Each variable that needs merging gets its own block parameter slot. |
|
| 2740 | 2733 | let mut paramIdx: u32 = 0; |
|
| 2741 | 2734 | for i in 0..blk.paramVars.len { |
|
| 2742 | - | if blk.paramVars[i] == v.id { |
|
| 2735 | + | if blk.paramVars[i] == *v { |
|
| 2743 | 2736 | paramIdx = i; |
|
| 2744 | 2737 | break; |
|
| 2745 | 2738 | } |
|
| 2746 | 2739 | } |
|
| 2747 | 2740 | ||
| 2748 | 2741 | // For each predecessor, recursively look up the variable's reaching definition |
|
| 2749 | 2742 | // in that block, then patch the predecessor's terminator to pass that value |
|
| 2750 | 2743 | // as an argument to this block's parameter. |
|
| 2751 | 2744 | for predId in blk.preds { |
|
| 2752 | - | let pred = BlockId { n: predId }; |
|
| 2745 | + | let pred = BlockId(predId); |
|
| 2753 | 2746 | // This may recursively trigger more block arg resolution if the |
|
| 2754 | 2747 | // predecessor also needs to look up the variable from its predecessors. |
|
| 2755 | 2748 | let val = try useVarInBlock(self, pred, v); |
|
| 2756 | 2749 | assert val != il::Val::Undef, "createBlockParam: predecessor provides undef value for block parameter"; |
|
| 2757 | - | patchTerminatorArg(self, pred, block.n, paramIdx, val); |
|
| 2750 | + | patchTerminatorArg(self, pred, *block, paramIdx, val); |
|
| 2758 | 2751 | } |
|
| 2759 | 2752 | } |
|
| 2760 | 2753 | ||
| 2761 | 2754 | /// Check if a block parameter is trivial, i.e. all predecessors provide |
|
| 2762 | 2755 | /// the same value. Returns the trivial value if so. |
|
| 2763 | 2756 | fn getTrivialPhiVal(self: *mut FnLowerer, block: BlockId, v: Var) -> ?il::Val throws (LowerError) { |
|
| 2764 | 2757 | let blk = getBlock(self, block); |
|
| 2765 | 2758 | // Get the block parameter register. |
|
| 2766 | - | let paramReg = blk.vars[v.id]; |
|
| 2759 | + | let paramReg = blk.vars[*v]; |
|
| 2767 | 2760 | // Check if all predecessors provide the same value. |
|
| 2768 | 2761 | let mut sameVal: ?il::Val = nil; |
|
| 2769 | 2762 | ||
| 2770 | 2763 | for predId in blk.preds { |
|
| 2771 | - | let pred = BlockId { n: predId }; |
|
| 2764 | + | let pred = BlockId(predId); |
|
| 2772 | 2765 | let val = try useVarInBlock(self, pred, v); |
|
| 2773 | 2766 | ||
| 2774 | 2767 | // Check if this is a self-reference. |
|
| 2775 | 2768 | // This happens in cycles where the loop back-edge passes the phi to |
|
| 2776 | 2769 | // itself. We skip self-references when checking for trivial phis. |
| 3362 | 3355 | let cv = resolver::constValueEntry(self.low.resolver, pat) |
|
| 3363 | 3356 | else throw LowerError::MissingConst(pat); |
|
| 3364 | 3357 | ||
| 3365 | 3358 | cases.append(il::SwitchCase { |
|
| 3366 | 3359 | value: constToScalar(cv), |
|
| 3367 | - | target: blocks[i].n, |
|
| 3360 | + | target: *blocks[i], |
|
| 3368 | 3361 | args: &mut [] |
|
| 3369 | 3362 | }, self.allocator); |
|
| 3370 | 3363 | } |
|
| 3371 | 3364 | } |
|
| 3372 | 3365 | } |
|
| 3373 | 3366 | addPredecessor(self, blocks[i], entry); |
|
| 3374 | 3367 | } |
|
| 3375 | 3368 | emit(self, il::Instr::Switch { |
|
| 3376 | 3369 | val: subject.val, |
|
| 3377 | - | defaultTarget: blocks[defaultIdx].n, |
|
| 3370 | + | defaultTarget: *blocks[defaultIdx], |
|
| 3378 | 3371 | defaultArgs: &mut [], |
|
| 3379 | 3372 | cases: &mut cases[..] |
|
| 3380 | 3373 | }); |
|
| 3381 | 3374 | ||
| 3382 | 3375 | for p, i in prongs { |
| 3517 | 3510 | } |
|
| 3518 | 3511 | ||
| 3519 | 3512 | // Evaluate guard if present; can still fail to next arm. |
|
| 3520 | 3513 | if let g = prong.guard { |
|
| 3521 | 3514 | try emitCondBranch(self, g, bodyBlock, nextArm); |
|
| 3522 | - | } else if currentBlock(self).n != bodyBlock.n { |
|
| 3515 | + | } else if *currentBlock(self) != *bodyBlock { |
|
| 3523 | 3516 | // Nested tests changed the current block. Create a new body block |
|
| 3524 | 3517 | // after the nest blocks to maintain RPO ordering, and jump to it. |
|
| 3525 | 3518 | bodyBlock = try createBlock(self, bodyLabel); |
|
| 3526 | 3519 | try emitJmp(self, bodyBlock); |
|
| 3527 | 3520 | } |
| 3622 | 3615 | } |
|
| 3623 | 3616 | // Handle guard: on success jump to `successBlock`, on failure jump to `failBlock`. |
|
| 3624 | 3617 | if let g = pat.guard { |
|
| 3625 | 3618 | try emitCondBranch(self, g, *successBlock, failBlock); |
|
| 3626 | 3619 | try switchToAndSeal(self, *successBlock); |
|
| 3627 | - | } else if currentBlock(self).n != targetBlock.n { |
|
| 3620 | + | } else if *currentBlock(self) != *targetBlock { |
|
| 3628 | 3621 | // Nested tests changed the current block. Create a new success block |
|
| 3629 | 3622 | // after the nest blocks to maintain RPO ordering, and jump to it. |
|
| 3630 | 3623 | *successBlock = try createBlock(self, successLabel); |
|
| 3631 | 3624 | ||
| 3632 | 3625 | try emitJmp(self, *successBlock); |
| 4300 | 4293 | let trueArgs = try allocVal(self, il::Val::Imm(1)); |
|
| 4301 | 4294 | ||
| 4302 | 4295 | // Check if tags differ. |
|
| 4303 | 4296 | emit(self, il::Instr::Br { |
|
| 4304 | 4297 | op: il::CmpOp::Eq, typ: il::Type::W8, a: tagA, b: tagB, |
|
| 4305 | - | thenTarget: nilCheck.n, thenArgs: &mut [], |
|
| 4306 | - | elseTarget: mergeBlock.n, elseArgs: falseArgs, |
|
| 4298 | + | thenTarget: *nilCheck, thenArgs: &mut [], |
|
| 4299 | + | elseTarget: *mergeBlock, elseArgs: falseArgs, |
|
| 4307 | 4300 | }); |
|
| 4308 | 4301 | addPredecessor(self, nilCheck, currentBlock(self)); |
|
| 4309 | 4302 | addPredecessor(self, mergeBlock, currentBlock(self)); |
|
| 4310 | 4303 | ||
| 4311 | 4304 | // Check if both are `nil`. |
|
| 4312 | 4305 | try switchToAndSeal(self, nilCheck); |
|
| 4313 | 4306 | emit(self, il::Instr::Br { |
|
| 4314 | 4307 | op: il::CmpOp::Ne, typ: il::Type::W8, a: tagA, b: il::Val::Imm(0), |
|
| 4315 | - | thenTarget: payloadCmp.n, thenArgs: &mut [], |
|
| 4316 | - | elseTarget: mergeBlock.n, elseArgs: trueArgs, |
|
| 4308 | + | thenTarget: *payloadCmp, thenArgs: &mut [], |
|
| 4309 | + | elseTarget: *mergeBlock, elseArgs: trueArgs, |
|
| 4317 | 4310 | }); |
|
| 4318 | 4311 | addPredecessor(self, payloadCmp, currentBlock(self)); |
|
| 4319 | 4312 | addPredecessor(self, mergeBlock, currentBlock(self)); |
|
| 4320 | 4313 | ||
| 4321 | 4314 | // Both are non-`nil`, compare payloads. |
| 4381 | 4374 | assert tagBlock != mergeBlock; |
|
| 4382 | 4375 | ||
| 4383 | 4376 | // TODO: Use the helper once the compiler supports more than eight function params. |
|
| 4384 | 4377 | emit(self, il::Instr::Br { |
|
| 4385 | 4378 | op: il::CmpOp::Eq, typ: il::Type::W8, a: tagA, b: tagB, |
|
| 4386 | - | thenTarget: tagBlock.n, thenArgs: &mut [], |
|
| 4387 | - | elseTarget: mergeBlock.n, elseArgs: falseArgs, |
|
| 4379 | + | thenTarget: *tagBlock, thenArgs: &mut [], |
|
| 4380 | + | elseTarget: *mergeBlock, elseArgs: falseArgs, |
|
| 4388 | 4381 | }); |
|
| 4389 | 4382 | addPredecessor(self, tagBlock, currentBlock(self)); |
|
| 4390 | 4383 | addPredecessor(self, mergeBlock, currentBlock(self)); |
|
| 4391 | 4384 | ||
| 4392 | 4385 | // Create comparison blocks for each non-void variant and build switch cases. |
| 4399 | 4392 | let mut caseBlocks: [?BlockId; resolver::MAX_UNION_VARIANTS] = undefined; |
|
| 4400 | 4393 | for variant, i in unionInfo.variants { |
|
| 4401 | 4394 | if variant.valueType == resolver::Type::Void { |
|
| 4402 | 4395 | cases[i] = il::SwitchCase { |
|
| 4403 | 4396 | value: i as i64, |
|
| 4404 | - | target: mergeBlock.n, |
|
| 4397 | + | target: *mergeBlock, |
|
| 4405 | 4398 | args: trueArgs |
|
| 4406 | 4399 | }; |
|
| 4407 | 4400 | caseBlocks[i] = nil; |
|
| 4408 | 4401 | } else { |
|
| 4409 | 4402 | let payloadBlock = try createBlock(self, "eq#payload"); |
|
| 4410 | 4403 | cases[i] = il::SwitchCase { |
|
| 4411 | 4404 | value: i as i64, |
|
| 4412 | - | target: payloadBlock.n, |
|
| 4405 | + | target: *payloadBlock, |
|
| 4413 | 4406 | args: &mut [] |
|
| 4414 | 4407 | }; |
|
| 4415 | 4408 | caseBlocks[i] = payloadBlock; |
|
| 4416 | 4409 | } |
|
| 4417 | 4410 | } |
| 4419 | 4412 | // Emit switch in @tag block. Default arm is unreachable since we cover all variants. |
|
| 4420 | 4413 | let unreachableBlock = try createBlock(self, "eq#unreachable"); |
|
| 4421 | 4414 | try switchToAndSeal(self, tagBlock); |
|
| 4422 | 4415 | emit(self, il::Instr::Switch { |
|
| 4423 | 4416 | val: tagA, |
|
| 4424 | - | defaultTarget: unreachableBlock.n, |
|
| 4417 | + | defaultTarget: *unreachableBlock, |
|
| 4425 | 4418 | defaultArgs: &mut [], |
|
| 4426 | 4419 | cases |
|
| 4427 | 4420 | }); |
|
| 4428 | 4421 | ||
| 4429 | 4422 | // Add predecessor edges for switch targets. |
| 4794 | 4787 | if isAggregateType(typ) { |
|
| 4795 | 4788 | return val; |
|
| 4796 | 4789 | } |
|
| 4797 | 4790 | // For scalars, if we've already materialized a stack slot for this |
|
| 4798 | 4791 | // variable, the SSA value is that slot pointer. |
|
| 4799 | - | if self.vars[v.id].addressTaken { |
|
| 4792 | + | if self.vars[*v].addressTaken { |
|
| 4800 | 4793 | // Already address-taken; return existing stack pointer. |
|
| 4801 | 4794 | return val; |
|
| 4802 | 4795 | } |
|
| 4803 | 4796 | // Materialize a stack slot using the declaration's resolved |
|
| 4804 | 4797 | // layout so `align(N)` on locals is honored. |
|
| 4805 | 4798 | let layout = resolver::getLayout(self.low.resolver, addr.target, typ); |
|
| 4806 | 4799 | let slot = emitReserveLayout(self, layout); |
|
| 4807 | 4800 | try emitStore(self, slot, 0, typ, val); |
|
| 4808 | 4801 | let stackVal = il::Val::Reg(slot); |
|
| 4809 | 4802 | ||
| 4810 | - | self.vars[v.id].addressTaken = true; |
|
| 4803 | + | self.vars[*v].addressTaken = true; |
|
| 4811 | 4804 | defVar(self, v, stackVal); |
|
| 4812 | 4805 | ||
| 4813 | 4806 | return stackVal; |
|
| 4814 | 4807 | } |
|
| 4815 | 4808 | // Fall back to symbol lookup for constants/statics. |
| 5004 | 4997 | let layout = resolver::getLayout(self.low.resolver, node, typ); |
|
| 5005 | 4998 | let slot = emitReserveLayout(self, layout); |
|
| 5006 | 4999 | try emitStore(self, slot, 0, typ, varVal); |
|
| 5007 | 5000 | ||
| 5008 | 5001 | let v = newVar(self, name, ilType, l.mutable, il::Val::Reg(slot)); |
|
| 5009 | - | self.vars[v.id].addressTaken = true; |
|
| 5002 | + | self.vars[*v].addressTaken = true; |
|
| 5010 | 5003 | ||
| 5011 | 5004 | return; |
|
| 5012 | 5005 | } |
|
| 5013 | 5006 | } |
|
| 5014 | 5007 | } |
| 6110 | 6103 | let errTy = try typeOf(self, typeNode); |
|
| 6111 | 6104 | errTypes[i] = errTy; |
|
| 6112 | 6105 | ||
| 6113 | 6106 | cases.append(il::SwitchCase { |
|
| 6114 | 6107 | value: getOrAssignErrorTag(self.low, errTy) as i64, |
|
| 6115 | - | target: blocks[i].n, |
|
| 6108 | + | target: *blocks[i], |
|
| 6116 | 6109 | args: &mut [] |
|
| 6117 | 6110 | }, self.allocator); |
|
| 6118 | 6111 | } else { |
|
| 6119 | 6112 | errTypes[i] = nil; |
|
| 6120 | 6113 | defaultIdx = i; |
| 6129 | 6122 | defaultTarget = try createBlock(self, "unreachable"); |
|
| 6130 | 6123 | addPredecessor(self, defaultTarget, entry); |
|
| 6131 | 6124 | } |
|
| 6132 | 6125 | emit(self, il::Instr::Switch { |
|
| 6133 | 6126 | val: il::Val::Reg(tagReg), |
|
| 6134 | - | defaultTarget: defaultTarget.n, |
|
| 6127 | + | defaultTarget: *defaultTarget, |
|
| 6135 | 6128 | defaultArgs: &mut [], |
|
| 6136 | 6129 | cases |
|
| 6137 | 6130 | }); |
|
| 6138 | 6131 | ||
| 6139 | 6132 | // Second pass: emit each catch clause body. |
| 6826 | 6819 | case ast::NodeValue::Ident(_) => { |
|
| 6827 | 6820 | // First try local variable lookup. |
|
| 6828 | 6821 | // Otherwise fall back to global symbol lookup. |
|
| 6829 | 6822 | if let v = lookupLocalVar(self, node) { |
|
| 6830 | 6823 | val = try useVar(self, v); |
|
| 6831 | - | if self.vars[v.id].addressTaken { |
|
| 6824 | + | if self.vars[*v].addressTaken { |
|
| 6832 | 6825 | let typ = try typeOf(self, node); |
|
| 6833 | 6826 | let ptr = emitValToReg(self, val); |
|
| 6834 | 6827 | val = emitRead(self, ptr, 0, typ); |
|
| 6835 | 6828 | } |
|
| 6836 | 6829 | } else { |
lib/std/sys/unix.rad
+9 -14
| 1 | 1 | //! Unix-specific system calls and utilities. |
|
| 2 | 2 | use std::intrinsics; |
|
| 3 | 3 | ||
| 4 | 4 | /// File access modes. |
|
| 5 | - | // TODO: Use unlabeled record (blocked on positional field access syntax). |
|
| 6 | - | pub record OpenFlags { |
|
| 7 | - | value: i64, |
|
| 8 | - | } |
|
| 5 | + | pub record OpenFlags(i64); |
|
| 9 | 6 | ||
| 10 | 7 | /// Open file for reading only. |
|
| 11 | - | pub const O_RDONLY: OpenFlags = OpenFlags { value: 0 }; |
|
| 8 | + | pub const O_RDONLY: OpenFlags = OpenFlags(0); |
|
| 12 | 9 | ||
| 13 | 10 | /// Open file for writing only. |
|
| 14 | - | pub const O_WRONLY: OpenFlags = OpenFlags { value: 1 }; |
|
| 11 | + | pub const O_WRONLY: OpenFlags = OpenFlags(1); |
|
| 15 | 12 | ||
| 16 | 13 | /// Open file for reading and writing. |
|
| 17 | - | pub const O_RDWR: OpenFlags = OpenFlags { value: 2 }; |
|
| 14 | + | pub const O_RDWR: OpenFlags = OpenFlags(2); |
|
| 18 | 15 | ||
| 19 | 16 | /// Create file if it doesn't exist. |
|
| 20 | - | pub const O_CREAT: OpenFlags = OpenFlags { value: 64 }; |
|
| 17 | + | pub const O_CREAT: OpenFlags = OpenFlags(64); |
|
| 21 | 18 | ||
| 22 | 19 | /// Truncate file to zero length. |
|
| 23 | - | pub const O_TRUNC: OpenFlags = OpenFlags { value: 512 }; |
|
| 20 | + | pub const O_TRUNC: OpenFlags = OpenFlags(512); |
|
| 24 | 21 | ||
| 25 | 22 | /// Standard file descriptor for stdin. |
|
| 26 | 23 | pub const STDIN: i64 = 0; |
|
| 27 | 24 | ||
| 28 | 25 | /// Standard file descriptor for stdout. |
| 35 | 32 | const AT_FDCWD: i64 = -100; |
|
| 36 | 33 | ||
| 37 | 34 | /// Opens a file at the given path and returns a file descriptor. |
|
| 38 | 35 | /// Returns a negative value on error. |
|
| 39 | 36 | pub fn open(path: *[u8], flags: OpenFlags) -> i64 { |
|
| 40 | - | return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, flags.value, 0); |
|
| 37 | + | return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, 0); |
|
| 41 | 38 | } |
|
| 42 | 39 | ||
| 43 | 40 | /// Opens a file at the given path with mode, returns a file descriptor. |
|
| 44 | 41 | pub fn openOpts(path: *[u8], flags: OpenFlags, mode: i64) -> i64 { |
|
| 45 | - | return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, flags.value, mode); |
|
| 42 | + | return intrinsics::ecall(56, AT_FDCWD, path.ptr as i64, *flags, mode); |
|
| 46 | 43 | } |
|
| 47 | 44 | ||
| 48 | 45 | /// Reads from a file descriptor into the provided buffer. |
|
| 49 | 46 | /// Returns the number of bytes read, or a negative value on error. |
|
| 50 | 47 | pub fn read(fd: i64, buf: *mut [u8]) -> i64 { |
| 107 | 104 | ||
| 108 | 105 | /// Writes the entire contents of a buffer to a file at the given path. |
|
| 109 | 106 | /// Creates the file if it doesn't exist, truncates if it does. |
|
| 110 | 107 | /// Returns `true` on success. |
|
| 111 | 108 | pub fn writeFile(path: *[u8], data: *[u8]) -> bool { |
|
| 112 | - | let flags = OpenFlags { |
|
| 113 | - | value: O_WRONLY.value | O_CREAT.value | O_TRUNC.value |
|
| 114 | - | }; |
|
| 109 | + | let flags = OpenFlags(*O_WRONLY | *O_CREAT | *O_TRUNC); |
|
| 115 | 110 | let fd = openOpts(path, flags, 420); // 0644 in octal. |
|
| 116 | 111 | if fd < 0 { |
|
| 117 | 112 | return false; |
|
| 118 | 113 | } |
|
| 119 | 114 | let mut written: u32 = 0; |