github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/wazevo/backend/compiler_lower.go (about)

     1  package backend
     2  
     3  import (
     4  	"github.com/wasilibs/wazerox/internal/engine/wazevo/backend/regalloc"
     5  	"github.com/wasilibs/wazerox/internal/engine/wazevo/ssa"
     6  )
     7  
     8  // Lower implements Compiler.Lower.
     9  func (c *compiler) Lower() {
    10  	c.assignVirtualRegisters()
    11  	c.mach.InitializeABI(c.ssaBuilder.Signature())
    12  	c.mach.StartLoweringFunction(c.ssaBuilder.BlockIDMax())
    13  	c.lowerBlocks()
    14  	c.mach.EndLoweringFunction()
    15  }
    16  
    17  // lowerBlocks lowers each block in the ssa.Builder.
    18  func (c *compiler) lowerBlocks() {
    19  	builder := c.ssaBuilder
    20  	for blk := builder.BlockIteratorReversePostOrderBegin(); blk != nil; blk = builder.BlockIteratorReversePostOrderNext() {
    21  		c.lowerBlock(blk)
    22  	}
    23  	// After lowering all blocks, we need to link adjacent blocks to layout one single instruction list.
    24  	var prev ssa.BasicBlock
    25  	for next := builder.BlockIteratorReversePostOrderBegin(); next != nil; next = builder.BlockIteratorReversePostOrderNext() {
    26  		if prev != nil {
    27  			c.mach.LinkAdjacentBlocks(prev, next)
    28  		}
    29  		prev = next
    30  	}
    31  }
    32  
    33  func (c *compiler) lowerBlock(blk ssa.BasicBlock) {
    34  	mach := c.mach
    35  	mach.StartBlock(blk)
    36  
    37  	// We traverse the instructions in reverse order because we might want to lower multiple
    38  	// instructions together.
    39  	cur := blk.Tail()
    40  
    41  	// First gather the branching instructions at the end of the blocks.
    42  	var br0, br1 *ssa.Instruction
    43  	if cur.IsBranching() {
    44  		br0 = cur
    45  		cur = cur.Prev()
    46  		if cur != nil && cur.IsBranching() {
    47  			br1 = cur
    48  			cur = cur.Prev()
    49  		}
    50  	}
    51  
    52  	if br0 != nil {
    53  		c.lowerBranches(br0, br1)
    54  	}
    55  
    56  	if br1 != nil && br0 == nil {
    57  		panic("BUG? when a block has conditional branch but doesn't end with an unconditional branch?")
    58  	}
    59  
    60  	// Now start lowering the non-branching instructions.
    61  	for ; cur != nil; cur = cur.Prev() {
    62  		c.setCurrentGroupID(cur.GroupID())
    63  		if cur.Lowered() {
    64  			continue
    65  		}
    66  
    67  		switch cur.Opcode() {
    68  		case ssa.OpcodeReturn:
    69  			c.lowerFunctionReturns(cur.ReturnVals())
    70  			c.mach.InsertReturn()
    71  		default:
    72  			mach.LowerInstr(cur)
    73  		}
    74  		mach.FlushPendingInstructions()
    75  	}
    76  
    77  	// Finally, if this is the entry block, we have to insert copies of arguments from the real location to the VReg.
    78  	if blk.EntryBlock() {
    79  		c.lowerFunctionArguments(blk)
    80  	}
    81  
    82  	mach.EndBlock()
    83  }
    84  
    85  // lowerBranches is called right after StartBlock and before any LowerInstr call if
    86  // there are branches to the given block. br0 is the very end of the block and b1 is the before the br0 if it exists.
    87  // At least br0 is not nil, but br1 can be nil if there's no branching before br0.
    88  //
    89  // See ssa.Instruction IsBranching, and the comment on ssa.BasicBlock.
    90  func (c *compiler) lowerBranches(br0, br1 *ssa.Instruction) {
    91  	c.setCurrentGroupID(br0.GroupID())
    92  	c.mach.LowerSingleBranch(br0)
    93  	c.mach.FlushPendingInstructions()
    94  	if br1 != nil {
    95  		c.setCurrentGroupID(br1.GroupID())
    96  		c.mach.LowerConditionalBranch(br1)
    97  		c.mach.FlushPendingInstructions()
    98  	}
    99  
   100  	if br0.Opcode() == ssa.OpcodeJump {
   101  		_, args, target := br0.BranchData()
   102  		argExists := len(args) != 0
   103  		if argExists && br1 != nil {
   104  			panic("BUG: critical edge split failed")
   105  		}
   106  		if argExists && target.ReturnBlock() {
   107  			c.lowerFunctionReturns(args)
   108  		} else if argExists {
   109  			c.lowerBlockArguments(args, target)
   110  		}
   111  	}
   112  	c.mach.FlushPendingInstructions()
   113  }
   114  
   115  func (c *compiler) lowerFunctionArguments(entry ssa.BasicBlock) {
   116  	c.tmpVals = c.tmpVals[:0]
   117  	for i := 0; i < entry.Params(); i++ {
   118  		p := entry.Param(i)
   119  		if c.ssaValueRefCounts[p.ID()] > 0 {
   120  			c.tmpVals = append(c.tmpVals, p)
   121  		} else {
   122  			// If the argument is not used, we can just pass an invalid value.
   123  			c.tmpVals = append(c.tmpVals, ssa.ValueInvalid)
   124  		}
   125  	}
   126  	c.mach.ABI().CalleeGenFunctionArgsToVRegs(c.tmpVals)
   127  	c.mach.FlushPendingInstructions()
   128  }
   129  
   130  func (c *compiler) lowerFunctionReturns(returns []ssa.Value) {
   131  	c.mach.ABI().CalleeGenVRegsToFunctionReturns(returns)
   132  }
   133  
   134  // lowerBlockArguments lowers how to pass arguments to the given successor block.
   135  func (c *compiler) lowerBlockArguments(args []ssa.Value, succ ssa.BasicBlock) {
   136  	if len(args) != succ.Params() {
   137  		panic("BUG: mismatched number of arguments")
   138  	}
   139  
   140  	c.varEdges = c.varEdges[:0]
   141  	c.varEdgeTypes = c.varEdgeTypes[:0]
   142  	c.constEdges = c.constEdges[:0]
   143  	for i := 0; i < len(args); i++ {
   144  		dst := succ.Param(i)
   145  		src := args[i]
   146  
   147  		dstReg := c.VRegOf(dst)
   148  		srcDef := c.ssaValueDefinitions[src.ID()]
   149  		if srcDef.IsFromInstr() && srcDef.Instr.Constant() {
   150  			c.constEdges = append(c.constEdges, struct {
   151  				cInst *ssa.Instruction
   152  				dst   regalloc.VReg
   153  			}{cInst: srcDef.Instr, dst: dstReg})
   154  		} else {
   155  			srcReg := c.VRegOf(src)
   156  			// Even when the src=dst, insert the move so that we can keep such registers keep-alive.
   157  			c.varEdges = append(c.varEdges, [2]regalloc.VReg{srcReg, dstReg})
   158  			c.varEdgeTypes = append(c.varEdgeTypes, src.Type())
   159  		}
   160  	}
   161  
   162  	// Check if there's an overlap among the dsts and srcs in varEdges.
   163  	c.vRegIDs = c.vRegIDs[:0]
   164  	for _, edge := range c.varEdges {
   165  		src := edge[0].ID()
   166  		if int(src) >= len(c.vRegSet) {
   167  			c.vRegSet = append(c.vRegSet, make([]bool, src+1)...)
   168  		}
   169  		c.vRegSet[src] = true
   170  		c.vRegIDs = append(c.vRegIDs, src)
   171  	}
   172  	separated := true
   173  	for _, edge := range c.varEdges {
   174  		dst := edge[1].ID()
   175  		if int(dst) >= len(c.vRegSet) {
   176  			c.vRegSet = append(c.vRegSet, make([]bool, dst+1)...)
   177  		} else {
   178  			if c.vRegSet[dst] {
   179  				separated = false
   180  				break
   181  			}
   182  		}
   183  	}
   184  	for _, id := range c.vRegIDs {
   185  		c.vRegSet[id] = false // reset for the next use.
   186  	}
   187  
   188  	if separated {
   189  		// If there's no overlap, we can simply move the source to destination.
   190  		for i, edge := range c.varEdges {
   191  			src, dst := edge[0], edge[1]
   192  			c.mach.InsertMove(dst, src, c.varEdgeTypes[i])
   193  		}
   194  	} else {
   195  		// Otherwise, we allocate a temporary registers and move the source to the temporary register,
   196  		//
   197  		// First move all of them to temporary registers.
   198  		c.tempRegs = c.tempRegs[:0]
   199  		for i, edge := range c.varEdges {
   200  			src := edge[0]
   201  			typ := c.varEdgeTypes[i]
   202  			temp := c.AllocateVReg(typ)
   203  			c.tempRegs = append(c.tempRegs, temp)
   204  			c.mach.InsertMove(temp, src, typ)
   205  		}
   206  		// Then move the temporary registers to the destination.
   207  		for i, edge := range c.varEdges {
   208  			temp := c.tempRegs[i]
   209  			dst := edge[1]
   210  			c.mach.InsertMove(dst, temp, c.varEdgeTypes[i])
   211  		}
   212  	}
   213  
   214  	// Finally, move the constants.
   215  	for _, edge := range c.constEdges {
   216  		cInst, dst := edge.cInst, edge.dst
   217  		c.mach.InsertLoadConstant(cInst, dst)
   218  	}
   219  }