github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/engine/wazevo/frontend/frontend.go (about)

     1  // Package frontend implements the translation of WebAssembly to SSA IR using the ssa package.
     2  package frontend
     3  
     4  import (
     5  	"bytes"
     6  	"math"
     7  
     8  	"github.com/tetratelabs/wazero/internal/engine/wazevo/ssa"
     9  	"github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
    10  	"github.com/tetratelabs/wazero/internal/wasm"
    11  )
    12  
    13  // Compiler is in charge of lowering Wasm to SSA IR, and does the optimization
    14  // on top of it in architecture-independent way.
    15  type Compiler struct {
    16  	// Per-module data that is used across all functions.
    17  
    18  	m      *wasm.Module
    19  	offset *wazevoapi.ModuleContextOffsetData
    20  	// ssaBuilder is a ssa.Builder used by this frontend.
    21  	ssaBuilder             ssa.Builder
    22  	signatures             map[*wasm.FunctionType]*ssa.Signature
    23  	listenerSignatures     map[*wasm.FunctionType][2]*ssa.Signature
    24  	memoryGrowSig          ssa.Signature
    25  	memoryWait32Sig        ssa.Signature
    26  	memoryWait64Sig        ssa.Signature
    27  	memoryNotifySig        ssa.Signature
    28  	checkModuleExitCodeSig ssa.Signature
    29  	tableGrowSig           ssa.Signature
    30  	refFuncSig             ssa.Signature
    31  	memmoveSig             ssa.Signature
    32  	ensureTermination      bool
    33  
    34  	// Followings are reset by per function.
    35  
    36  	// wasmLocalToVariable maps the index (considered as wasm.Index of locals)
    37  	// to the corresponding ssa.Variable.
    38  	wasmLocalToVariable                   [] /* local index to */ ssa.Variable
    39  	wasmLocalFunctionIndex                wasm.Index
    40  	wasmFunctionTypeIndex                 wasm.Index
    41  	wasmFunctionTyp                       *wasm.FunctionType
    42  	wasmFunctionLocalTypes                []wasm.ValueType
    43  	wasmFunctionBody                      []byte
    44  	wasmFunctionBodyOffsetInCodeSection   uint64
    45  	memoryBaseVariable, memoryLenVariable ssa.Variable
    46  	needMemory                            bool
    47  	memoryShared                          bool
    48  	globalVariables                       []ssa.Variable
    49  	globalVariablesTypes                  []ssa.Type
    50  	mutableGlobalVariablesIndexes         []wasm.Index // index to ^.
    51  	needListener                          bool
    52  	needSourceOffsetInfo                  bool
    53  	// br is reused during lowering.
    54  	br            *bytes.Reader
    55  	loweringState loweringState
    56  
    57  	knownSafeBounds    [] /* ssa.ValueID to */ knownSafeBound
    58  	knownSafeBoundsSet []ssa.ValueID
    59  
    60  	knownSafeBoundsAtTheEndOfBlocks   [] /* ssa.BlockID to */ knownSafeBoundsAtTheEndOfBlock
    61  	varLengthKnownSafeBoundWithIDPool wazevoapi.VarLengthPool[knownSafeBoundWithID]
    62  
    63  	execCtxPtrValue, moduleCtxPtrValue ssa.Value
    64  
    65  	// Following are reused for the known safe bounds analysis.
    66  
    67  	pointers []int
    68  	bounds   [][]knownSafeBoundWithID
    69  }
    70  
    71  type (
    72  	// knownSafeBound represents a known safe bound for a value.
    73  	knownSafeBound struct {
    74  		// bound is a constant upper bound for the value.
    75  		bound uint64
    76  		// absoluteAddr is the absolute address of the value.
    77  		absoluteAddr ssa.Value
    78  	}
    79  	// knownSafeBoundWithID is a knownSafeBound with the ID of the value.
    80  	knownSafeBoundWithID struct {
    81  		knownSafeBound
    82  		id ssa.ValueID
    83  	}
    84  	knownSafeBoundsAtTheEndOfBlock = wazevoapi.VarLength[knownSafeBoundWithID]
    85  )
    86  
    87  var knownSafeBoundsAtTheEndOfBlockNil = wazevoapi.NewNilVarLength[knownSafeBoundWithID]()
    88  
    89  // NewFrontendCompiler returns a frontend Compiler.
    90  func NewFrontendCompiler(m *wasm.Module, ssaBuilder ssa.Builder, offset *wazevoapi.ModuleContextOffsetData, ensureTermination bool, listenerOn bool, sourceInfo bool) *Compiler {
    91  	c := &Compiler{
    92  		m:                                 m,
    93  		ssaBuilder:                        ssaBuilder,
    94  		br:                                bytes.NewReader(nil),
    95  		offset:                            offset,
    96  		ensureTermination:                 ensureTermination,
    97  		needSourceOffsetInfo:              sourceInfo,
    98  		varLengthKnownSafeBoundWithIDPool: wazevoapi.NewVarLengthPool[knownSafeBoundWithID](),
    99  	}
   100  	c.declareSignatures(listenerOn)
   101  	return c
   102  }
   103  
   104  func (c *Compiler) declareSignatures(listenerOn bool) {
   105  	m := c.m
   106  	c.signatures = make(map[*wasm.FunctionType]*ssa.Signature, len(m.TypeSection)+2)
   107  	if listenerOn {
   108  		c.listenerSignatures = make(map[*wasm.FunctionType][2]*ssa.Signature, len(m.TypeSection))
   109  	}
   110  	for i := range m.TypeSection {
   111  		wasmSig := &m.TypeSection[i]
   112  		sig := SignatureForWasmFunctionType(wasmSig)
   113  		sig.ID = ssa.SignatureID(i)
   114  		c.signatures[wasmSig] = &sig
   115  		c.ssaBuilder.DeclareSignature(&sig)
   116  
   117  		if listenerOn {
   118  			beforeSig, afterSig := SignatureForListener(wasmSig)
   119  			beforeSig.ID = ssa.SignatureID(i) + ssa.SignatureID(len(m.TypeSection))
   120  			afterSig.ID = ssa.SignatureID(i) + ssa.SignatureID(len(m.TypeSection))*2
   121  			c.listenerSignatures[wasmSig] = [2]*ssa.Signature{beforeSig, afterSig}
   122  			c.ssaBuilder.DeclareSignature(beforeSig)
   123  			c.ssaBuilder.DeclareSignature(afterSig)
   124  		}
   125  	}
   126  
   127  	begin := ssa.SignatureID(len(m.TypeSection))
   128  	if listenerOn {
   129  		begin *= 3
   130  	}
   131  	c.memoryGrowSig = ssa.Signature{
   132  		ID: begin,
   133  		// Takes execution context and the page size to grow.
   134  		Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32},
   135  		// Returns the previous page size.
   136  		Results: []ssa.Type{ssa.TypeI32},
   137  	}
   138  	c.ssaBuilder.DeclareSignature(&c.memoryGrowSig)
   139  
   140  	c.checkModuleExitCodeSig = ssa.Signature{
   141  		ID: c.memoryGrowSig.ID + 1,
   142  		// Only takes execution context.
   143  		Params: []ssa.Type{ssa.TypeI64},
   144  	}
   145  	c.ssaBuilder.DeclareSignature(&c.checkModuleExitCodeSig)
   146  
   147  	c.tableGrowSig = ssa.Signature{
   148  		ID:     c.checkModuleExitCodeSig.ID + 1,
   149  		Params: []ssa.Type{ssa.TypeI64 /* exec context */, ssa.TypeI32 /* table index */, ssa.TypeI32 /* num */, ssa.TypeI64 /* ref */},
   150  		// Returns the previous size.
   151  		Results: []ssa.Type{ssa.TypeI32},
   152  	}
   153  	c.ssaBuilder.DeclareSignature(&c.tableGrowSig)
   154  
   155  	c.refFuncSig = ssa.Signature{
   156  		ID:     c.tableGrowSig.ID + 1,
   157  		Params: []ssa.Type{ssa.TypeI64 /* exec context */, ssa.TypeI32 /* func index */},
   158  		// Returns the function reference.
   159  		Results: []ssa.Type{ssa.TypeI64},
   160  	}
   161  	c.ssaBuilder.DeclareSignature(&c.refFuncSig)
   162  
   163  	c.memmoveSig = ssa.Signature{
   164  		ID: c.refFuncSig.ID + 1,
   165  		// dst, src, and the byte count.
   166  		Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64, ssa.TypeI64},
   167  	}
   168  
   169  	c.ssaBuilder.DeclareSignature(&c.memmoveSig)
   170  
   171  	c.memoryWait32Sig = ssa.Signature{
   172  		ID: c.memmoveSig.ID + 1,
   173  		// exec context, timeout, expected, addr
   174  		Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64, ssa.TypeI32, ssa.TypeI64},
   175  		// Returns the status.
   176  		Results: []ssa.Type{ssa.TypeI32},
   177  	}
   178  	c.ssaBuilder.DeclareSignature(&c.memoryWait32Sig)
   179  
   180  	c.memoryWait64Sig = ssa.Signature{
   181  		ID: c.memoryWait32Sig.ID + 1,
   182  		// exec context, timeout, expected, addr
   183  		Params: []ssa.Type{ssa.TypeI64, ssa.TypeI64, ssa.TypeI64, ssa.TypeI64},
   184  		// Returns the status.
   185  		Results: []ssa.Type{ssa.TypeI32},
   186  	}
   187  	c.ssaBuilder.DeclareSignature(&c.memoryWait64Sig)
   188  
   189  	c.memoryNotifySig = ssa.Signature{
   190  		ID: c.memoryWait64Sig.ID + 1,
   191  		// exec context, count, addr
   192  		Params: []ssa.Type{ssa.TypeI64, ssa.TypeI32, ssa.TypeI64},
   193  		// Returns the number notified.
   194  		Results: []ssa.Type{ssa.TypeI32},
   195  	}
   196  	c.ssaBuilder.DeclareSignature(&c.memoryNotifySig)
   197  }
   198  
   199  // SignatureForWasmFunctionType returns the ssa.Signature for the given wasm.FunctionType.
   200  func SignatureForWasmFunctionType(typ *wasm.FunctionType) ssa.Signature {
   201  	sig := ssa.Signature{
   202  		// +2 to pass moduleContextPtr and executionContextPtr. See the inline comment LowerToSSA.
   203  		Params:  make([]ssa.Type, len(typ.Params)+2),
   204  		Results: make([]ssa.Type, len(typ.Results)),
   205  	}
   206  	sig.Params[0] = executionContextPtrTyp
   207  	sig.Params[1] = moduleContextPtrTyp
   208  	for j, typ := range typ.Params {
   209  		sig.Params[j+2] = WasmTypeToSSAType(typ)
   210  	}
   211  	for j, typ := range typ.Results {
   212  		sig.Results[j] = WasmTypeToSSAType(typ)
   213  	}
   214  	return sig
   215  }
   216  
   217  // Init initializes the state of frontendCompiler and make it ready for a next function.
   218  func (c *Compiler) Init(idx, typIndex wasm.Index, typ *wasm.FunctionType, localTypes []wasm.ValueType, body []byte, needListener bool, bodyOffsetInCodeSection uint64) {
   219  	c.ssaBuilder.Init(c.signatures[typ])
   220  	c.loweringState.reset()
   221  
   222  	c.wasmFunctionTypeIndex = typIndex
   223  	c.wasmLocalFunctionIndex = idx
   224  	c.wasmFunctionTyp = typ
   225  	c.wasmFunctionLocalTypes = localTypes
   226  	c.wasmFunctionBody = body
   227  	c.wasmFunctionBodyOffsetInCodeSection = bodyOffsetInCodeSection
   228  	c.needListener = needListener
   229  	c.clearSafeBounds()
   230  	c.varLengthKnownSafeBoundWithIDPool.Reset()
   231  	c.knownSafeBoundsAtTheEndOfBlocks = c.knownSafeBoundsAtTheEndOfBlocks[:0]
   232  }
   233  
   234  // Note: this assumes 64-bit platform (I believe we won't have 32-bit backend ;)).
   235  const executionContextPtrTyp, moduleContextPtrTyp = ssa.TypeI64, ssa.TypeI64
   236  
   237  // LowerToSSA lowers the current function to SSA function which will be held by ssaBuilder.
   238  // After calling this, the caller will be able to access the SSA info in *Compiler.ssaBuilder.
   239  //
   240  // Note that this only does the naive lowering, and do not do any optimization, instead the caller is expected to do so.
   241  func (c *Compiler) LowerToSSA() {
   242  	builder := c.ssaBuilder
   243  
   244  	// Set up the entry block.
   245  	entryBlock := builder.AllocateBasicBlock()
   246  	builder.SetCurrentBlock(entryBlock)
   247  
   248  	// Functions always take two parameters in addition to Wasm-level parameters:
   249  	//
   250  	//  1. executionContextPtr: pointer to the *executionContext in wazevo package.
   251  	//    This will be used to exit the execution in the face of trap, plus used for host function calls.
   252  	//
   253  	// 	2. moduleContextPtr: pointer to the *moduleContextOpaque in wazevo package.
   254  	//	  This will be used to access memory, etc. Also, this will be used during host function calls.
   255  	//
   256  	// Note: it's clear that sometimes a function won't need them. For example,
   257  	//  if the function doesn't trap and doesn't make function call, then
   258  	// 	we might be able to eliminate the parameter. However, if that function
   259  	//	can be called via call_indirect, then we cannot eliminate because the
   260  	//  signature won't match with the expected one.
   261  	// TODO: maybe there's some way to do this optimization without glitches, but so far I have no clue about the feasibility.
   262  	//
   263  	// Note: In Wasmtime or many other runtimes, moduleContextPtr is called "vmContext". Also note that `moduleContextPtr`
   264  	//  is wazero-specific since other runtimes can naturally use the OS-level signal to do this job thanks to the fact that
   265  	//  they can use native stack vs wazero cannot use Go-routine stack and have to use Go-runtime allocated []byte as a stack.
   266  	c.execCtxPtrValue = entryBlock.AddParam(builder, executionContextPtrTyp)
   267  	c.moduleCtxPtrValue = entryBlock.AddParam(builder, moduleContextPtrTyp)
   268  	builder.AnnotateValue(c.execCtxPtrValue, "exec_ctx")
   269  	builder.AnnotateValue(c.moduleCtxPtrValue, "module_ctx")
   270  
   271  	for i, typ := range c.wasmFunctionTyp.Params {
   272  		st := WasmTypeToSSAType(typ)
   273  		variable := builder.DeclareVariable(st)
   274  		value := entryBlock.AddParam(builder, st)
   275  		builder.DefineVariable(variable, value, entryBlock)
   276  		c.setWasmLocalVariable(wasm.Index(i), variable)
   277  	}
   278  	c.declareWasmLocals(entryBlock)
   279  	c.declareNecessaryVariables()
   280  
   281  	c.lowerBody(entryBlock)
   282  }
   283  
   284  // localVariable returns the SSA variable for the given Wasm local index.
   285  func (c *Compiler) localVariable(index wasm.Index) ssa.Variable {
   286  	return c.wasmLocalToVariable[index]
   287  }
   288  
   289  func (c *Compiler) setWasmLocalVariable(index wasm.Index, variable ssa.Variable) {
   290  	idx := int(index)
   291  	if idx >= len(c.wasmLocalToVariable) {
   292  		c.wasmLocalToVariable = append(c.wasmLocalToVariable, make([]ssa.Variable, idx+1-len(c.wasmLocalToVariable))...)
   293  	}
   294  	c.wasmLocalToVariable[idx] = variable
   295  }
   296  
   297  // declareWasmLocals declares the SSA variables for the Wasm locals.
   298  func (c *Compiler) declareWasmLocals(entry ssa.BasicBlock) {
   299  	localCount := wasm.Index(len(c.wasmFunctionTyp.Params))
   300  	for i, typ := range c.wasmFunctionLocalTypes {
   301  		st := WasmTypeToSSAType(typ)
   302  		variable := c.ssaBuilder.DeclareVariable(st)
   303  		c.setWasmLocalVariable(wasm.Index(i)+localCount, variable)
   304  
   305  		zeroInst := c.ssaBuilder.AllocateInstruction()
   306  		switch st {
   307  		case ssa.TypeI32:
   308  			zeroInst.AsIconst32(0)
   309  		case ssa.TypeI64:
   310  			zeroInst.AsIconst64(0)
   311  		case ssa.TypeF32:
   312  			zeroInst.AsF32const(0)
   313  		case ssa.TypeF64:
   314  			zeroInst.AsF64const(0)
   315  		case ssa.TypeV128:
   316  			zeroInst.AsVconst(0, 0)
   317  		default:
   318  			panic("TODO: " + wasm.ValueTypeName(typ))
   319  		}
   320  
   321  		c.ssaBuilder.InsertInstruction(zeroInst)
   322  		value := zeroInst.Return()
   323  		c.ssaBuilder.DefineVariable(variable, value, entry)
   324  	}
   325  }
   326  
   327  func (c *Compiler) declareNecessaryVariables() {
   328  	if c.needMemory = c.m.MemorySection != nil; c.needMemory {
   329  		c.memoryShared = c.m.MemorySection.IsShared
   330  	} else if c.needMemory = c.m.ImportMemoryCount > 0; c.needMemory {
   331  		for _, imp := range c.m.ImportSection {
   332  			if imp.Type == wasm.ExternTypeMemory {
   333  				c.memoryShared = imp.DescMem.IsShared
   334  				break
   335  			}
   336  		}
   337  	}
   338  
   339  	if c.needMemory {
   340  		c.memoryBaseVariable = c.ssaBuilder.DeclareVariable(ssa.TypeI64)
   341  		c.memoryLenVariable = c.ssaBuilder.DeclareVariable(ssa.TypeI64)
   342  	}
   343  
   344  	c.globalVariables = c.globalVariables[:0]
   345  	c.mutableGlobalVariablesIndexes = c.mutableGlobalVariablesIndexes[:0]
   346  	c.globalVariablesTypes = c.globalVariablesTypes[:0]
   347  	for _, imp := range c.m.ImportSection {
   348  		if imp.Type == wasm.ExternTypeGlobal {
   349  			desc := imp.DescGlobal
   350  			c.declareWasmGlobal(desc.ValType, desc.Mutable)
   351  		}
   352  	}
   353  	for _, g := range c.m.GlobalSection {
   354  		desc := g.Type
   355  		c.declareWasmGlobal(desc.ValType, desc.Mutable)
   356  	}
   357  
   358  	// TODO: add tables.
   359  }
   360  
   361  func (c *Compiler) declareWasmGlobal(typ wasm.ValueType, mutable bool) {
   362  	var st ssa.Type
   363  	switch typ {
   364  	case wasm.ValueTypeI32:
   365  		st = ssa.TypeI32
   366  	case wasm.ValueTypeI64,
   367  		// Both externref and funcref are represented as I64 since we only support 64-bit platforms.
   368  		wasm.ValueTypeExternref, wasm.ValueTypeFuncref:
   369  		st = ssa.TypeI64
   370  	case wasm.ValueTypeF32:
   371  		st = ssa.TypeF32
   372  	case wasm.ValueTypeF64:
   373  		st = ssa.TypeF64
   374  	case wasm.ValueTypeV128:
   375  		st = ssa.TypeV128
   376  	default:
   377  		panic("TODO: " + wasm.ValueTypeName(typ))
   378  	}
   379  	v := c.ssaBuilder.DeclareVariable(st)
   380  	index := wasm.Index(len(c.globalVariables))
   381  	c.globalVariables = append(c.globalVariables, v)
   382  	c.globalVariablesTypes = append(c.globalVariablesTypes, st)
   383  	if mutable {
   384  		c.mutableGlobalVariablesIndexes = append(c.mutableGlobalVariablesIndexes, index)
   385  	}
   386  }
   387  
   388  // WasmTypeToSSAType converts wasm.ValueType to ssa.Type.
   389  func WasmTypeToSSAType(vt wasm.ValueType) ssa.Type {
   390  	switch vt {
   391  	case wasm.ValueTypeI32:
   392  		return ssa.TypeI32
   393  	case wasm.ValueTypeI64,
   394  		// Both externref and funcref are represented as I64 since we only support 64-bit platforms.
   395  		wasm.ValueTypeExternref, wasm.ValueTypeFuncref:
   396  		return ssa.TypeI64
   397  	case wasm.ValueTypeF32:
   398  		return ssa.TypeF32
   399  	case wasm.ValueTypeF64:
   400  		return ssa.TypeF64
   401  	case wasm.ValueTypeV128:
   402  		return ssa.TypeV128
   403  	default:
   404  		panic("TODO: " + wasm.ValueTypeName(vt))
   405  	}
   406  }
   407  
   408  // addBlockParamsFromWasmTypes adds the block parameters to the given block.
   409  func (c *Compiler) addBlockParamsFromWasmTypes(tps []wasm.ValueType, blk ssa.BasicBlock) {
   410  	for _, typ := range tps {
   411  		st := WasmTypeToSSAType(typ)
   412  		blk.AddParam(c.ssaBuilder, st)
   413  	}
   414  }
   415  
   416  // formatBuilder outputs the constructed SSA function as a string with a source information.
   417  func (c *Compiler) formatBuilder() string {
   418  	return c.ssaBuilder.Format()
   419  }
   420  
   421  // SignatureForListener returns the signatures for the listener functions.
   422  func SignatureForListener(wasmSig *wasm.FunctionType) (*ssa.Signature, *ssa.Signature) {
   423  	beforeSig := &ssa.Signature{}
   424  	beforeSig.Params = make([]ssa.Type, len(wasmSig.Params)+2)
   425  	beforeSig.Params[0] = ssa.TypeI64 // Execution context.
   426  	beforeSig.Params[1] = ssa.TypeI32 // Function index.
   427  	for i, p := range wasmSig.Params {
   428  		beforeSig.Params[i+2] = WasmTypeToSSAType(p)
   429  	}
   430  	afterSig := &ssa.Signature{}
   431  	afterSig.Params = make([]ssa.Type, len(wasmSig.Results)+2)
   432  	afterSig.Params[0] = ssa.TypeI64 // Execution context.
   433  	afterSig.Params[1] = ssa.TypeI32 // Function index.
   434  	for i, p := range wasmSig.Results {
   435  		afterSig.Params[i+2] = WasmTypeToSSAType(p)
   436  	}
   437  	return beforeSig, afterSig
   438  }
   439  
   440  // isBoundSafe returns true if the given value is known to be safe to access up to the given bound.
   441  func (c *Compiler) getKnownSafeBound(v ssa.ValueID) *knownSafeBound {
   442  	if int(v) >= len(c.knownSafeBounds) {
   443  		return nil
   444  	}
   445  	return &c.knownSafeBounds[v]
   446  }
   447  
   448  // recordKnownSafeBound records the given safe bound for the given value.
   449  func (c *Compiler) recordKnownSafeBound(v ssa.ValueID, safeBound uint64, absoluteAddr ssa.Value) {
   450  	if int(v) >= len(c.knownSafeBounds) {
   451  		c.knownSafeBounds = append(c.knownSafeBounds, make([]knownSafeBound, v+1)...)
   452  	}
   453  
   454  	if exiting := c.knownSafeBounds[v]; exiting.bound == 0 {
   455  		c.knownSafeBounds[v] = knownSafeBound{
   456  			bound:        safeBound,
   457  			absoluteAddr: absoluteAddr,
   458  		}
   459  		c.knownSafeBoundsSet = append(c.knownSafeBoundsSet, v)
   460  	} else if safeBound > exiting.bound {
   461  		c.knownSafeBounds[v].bound = safeBound
   462  	}
   463  }
   464  
   465  // clearSafeBounds clears the known safe bounds.
   466  func (c *Compiler) clearSafeBounds() {
   467  	for _, v := range c.knownSafeBoundsSet {
   468  		ptr := &c.knownSafeBounds[v]
   469  		ptr.bound = 0
   470  		ptr.absoluteAddr = ssa.ValueInvalid
   471  	}
   472  	c.knownSafeBoundsSet = c.knownSafeBoundsSet[:0]
   473  }
   474  
   475  // resetAbsoluteAddressInSafeBounds resets the absolute addresses recorded in the known safe bounds.
   476  func (c *Compiler) resetAbsoluteAddressInSafeBounds() {
   477  	for _, v := range c.knownSafeBoundsSet {
   478  		ptr := &c.knownSafeBounds[v]
   479  		ptr.absoluteAddr = ssa.ValueInvalid
   480  	}
   481  }
   482  
   483  func (k *knownSafeBound) valid() bool {
   484  	return k != nil && k.bound > 0
   485  }
   486  
   487  func (c *Compiler) allocateVarLengthValues(_cap int, vs ...ssa.Value) ssa.Values {
   488  	builder := c.ssaBuilder
   489  	pool := builder.VarLengthPool()
   490  	args := pool.Allocate(_cap)
   491  	args = args.Append(builder.VarLengthPool(), vs...)
   492  	return args
   493  }
   494  
   495  func (c *Compiler) finalizeKnownSafeBoundsAtTheEndOfBlock(bID ssa.BasicBlockID) {
   496  	_bID := int(bID)
   497  	if l := len(c.knownSafeBoundsAtTheEndOfBlocks); _bID >= l {
   498  		c.knownSafeBoundsAtTheEndOfBlocks = append(c.knownSafeBoundsAtTheEndOfBlocks,
   499  			make([]knownSafeBoundsAtTheEndOfBlock, _bID+1-len(c.knownSafeBoundsAtTheEndOfBlocks))...)
   500  		for i := l; i < len(c.knownSafeBoundsAtTheEndOfBlocks); i++ {
   501  			c.knownSafeBoundsAtTheEndOfBlocks[i] = knownSafeBoundsAtTheEndOfBlockNil
   502  		}
   503  	}
   504  	p := &c.varLengthKnownSafeBoundWithIDPool
   505  	size := len(c.knownSafeBoundsSet)
   506  	allocated := c.varLengthKnownSafeBoundWithIDPool.Allocate(size)
   507  	// Sort the known safe bounds by the value ID so that we can use the intersection algorithm in initializeCurrentBlockKnownBounds.
   508  	sortSSAValueIDs(c.knownSafeBoundsSet)
   509  	for _, vID := range c.knownSafeBoundsSet {
   510  		kb := c.knownSafeBounds[vID]
   511  		allocated = allocated.Append(p, knownSafeBoundWithID{
   512  			knownSafeBound: kb,
   513  			id:             vID,
   514  		})
   515  	}
   516  	c.knownSafeBoundsAtTheEndOfBlocks[bID] = allocated
   517  	c.clearSafeBounds()
   518  }
   519  
   520  func (c *Compiler) initializeCurrentBlockKnownBounds() {
   521  	currentBlk := c.ssaBuilder.CurrentBlock()
   522  	switch preds := currentBlk.Preds(); preds {
   523  	case 0:
   524  	case 1:
   525  		pred := currentBlk.Pred(0).ID()
   526  		for _, kb := range c.getKnownSafeBoundsAtTheEndOfBlocks(pred).View() {
   527  			// Unless the block is sealed, we cannot assume the absolute address is valid:
   528  			// later we might add another predecessor that has no visibility of that value.
   529  			addr := ssa.ValueInvalid
   530  			if currentBlk.Sealed() {
   531  				addr = kb.absoluteAddr
   532  			}
   533  			c.recordKnownSafeBound(kb.id, kb.bound, addr)
   534  		}
   535  	default:
   536  		c.pointers = c.pointers[:0]
   537  		c.bounds = c.bounds[:0]
   538  		for i := 0; i < preds; i++ {
   539  			c.bounds = append(c.bounds, c.getKnownSafeBoundsAtTheEndOfBlocks(currentBlk.Pred(i).ID()).View())
   540  			c.pointers = append(c.pointers, 0)
   541  		}
   542  
   543  		// If there are multiple predecessors, we need to find the intersection of the known safe bounds.
   544  
   545  	outer:
   546  		for {
   547  			smallestID := ssa.ValueID(math.MaxUint32)
   548  			for i, ptr := range c.pointers {
   549  				if ptr >= len(c.bounds[i]) {
   550  					break outer
   551  				}
   552  				cb := &c.bounds[i][ptr]
   553  				if id := cb.id; id < smallestID {
   554  					smallestID = cb.id
   555  				}
   556  			}
   557  
   558  			// Check if current elements are the same across all lists.
   559  			same := true
   560  			minBound := uint64(math.MaxUint64)
   561  			for i := 0; i < preds; i++ {
   562  				cb := &c.bounds[i][c.pointers[i]]
   563  				if cb.id != smallestID {
   564  					same = false
   565  					break
   566  				} else {
   567  					if cb.bound < minBound {
   568  						minBound = cb.bound
   569  					}
   570  				}
   571  			}
   572  
   573  			if same { // All elements are the same.
   574  				// Absolute address cannot be used in the intersection since the value might be only defined in one of the predecessors.
   575  				c.recordKnownSafeBound(smallestID, minBound, ssa.ValueInvalid)
   576  			}
   577  
   578  			// Move pointer(s) for the smallest ID forward (if same, move all).
   579  			for i := 0; i < preds; i++ {
   580  				cb := &c.bounds[i][c.pointers[i]]
   581  				if cb.id == smallestID {
   582  					c.pointers[i]++
   583  				}
   584  			}
   585  		}
   586  	}
   587  }
   588  
   589  func (c *Compiler) getKnownSafeBoundsAtTheEndOfBlocks(id ssa.BasicBlockID) knownSafeBoundsAtTheEndOfBlock {
   590  	if int(id) >= len(c.knownSafeBoundsAtTheEndOfBlocks) {
   591  		return knownSafeBoundsAtTheEndOfBlockNil
   592  	}
   593  	return c.knownSafeBoundsAtTheEndOfBlocks[id]
   594  }