diff --git a/internal/engine/wazevo/ssa/builder.go b/internal/engine/wazevo/ssa/builder.go index 60deb535c7..950e5767e3 100644 --- a/internal/engine/wazevo/ssa/builder.go +++ b/internal/engine/wazevo/ssa/builder.go @@ -182,11 +182,10 @@ type builder struct { loopNestingForestRoots []BasicBlock // The followings are used for optimization passes/deterministic compilation. - instStack []*Instruction - valueIDToInstruction []*Instruction - blkStack []*basicBlock - blkStack2 []*basicBlock - redundantParams []redundantParam + instStack []*Instruction + blkStack []*basicBlock + blkStack2 []*basicBlock + redundantParams []redundantParam // blockIterCur is used to implement blockIteratorBegin and blockIteratorNext. blockIterCur int @@ -291,7 +290,6 @@ func (b *builder) Init(s *Signature) { for v := ValueID(0); v < b.nextValueID; v++ { delete(b.valueAnnotations, v) b.valuesInfo[v] = ValueInfo{alias: ValueInvalid} - b.valueIDToInstruction[v] = nil } b.nextValueID = 0 b.reversePostOrderedBasicBlocks = b.reversePostOrderedBasicBlocks[:0] @@ -397,7 +395,7 @@ func (b *builder) InsertInstruction(instr *Instruction) { } r1 := b.allocateValue(t1) - instr.rValue = r1 + instr.rValue = r1.setInstructionID(instr.id) tsl := len(ts) if tsl == 0 { @@ -406,7 +404,8 @@ func (b *builder) InsertInstruction(instr *Instruction) { rValues := b.varLengthPool.Allocate(tsl) for i := 0; i < tsl; i++ { - rValues = rValues.Append(&b.varLengthPool, b.allocateValue(ts[i])) + rn := b.allocateValue(ts[i]) + rValues = rValues.Append(&b.varLengthPool, rn.setInstructionID(instr.id)) } instr.rValues = rValues } @@ -776,3 +775,13 @@ func (b *builder) LoopNestingForestRoots() []BasicBlock { func (b *builder) LowestCommonAncestor(blk1, blk2 BasicBlock) BasicBlock { return b.sparseTree.findLCA(blk1.ID(), blk2.ID()) } + +// instructionOfValue returns the instruction that produces the given Value, or nil +// if the Value is not produced by any instruction. +func (b *builder) instructionOfValue(v Value) *Instruction { + instrID := v.instructionID() + if instrID <= 0 { + return nil + } + return b.instructionsPool.View(instrID - 1) +} diff --git a/internal/engine/wazevo/ssa/pass.go b/internal/engine/wazevo/ssa/pass.go index 64dc5f5c85..6ee26a3e60 100644 --- a/internal/engine/wazevo/ssa/pass.go +++ b/internal/engine/wazevo/ssa/pass.go @@ -243,9 +243,6 @@ func passDeadCodeEliminationOpt(b *builder) { view[i].alias = ValueInvalid } } - if nvid >= len(b.valueIDToInstruction) { - b.valueIDToInstruction = append(b.valueIDToInstruction, make([]*Instruction, nvid-len(b.valueIDToInstruction)+1)...) - } // First, we gather all the instructions with side effects. liveInstructions := b.instStack[:0] @@ -264,14 +261,6 @@ func passDeadCodeEliminationOpt(b *builder) { // The strict side effect should create different instruction groups. gid++ } - - r1, rs := cur.Returns() - if r1.Valid() { - b.valueIDToInstruction[r1.ID()] = cur - } - for _, r := range rs { - b.valueIDToInstruction[r.ID()] = cur - } } } @@ -292,28 +281,28 @@ func passDeadCodeEliminationOpt(b *builder) { v1, v2, v3, vs := live.Args() if v1.Valid() { - producingInst := b.valueIDToInstruction[v1.ID()] + producingInst := b.instructionOfValue(v1) if producingInst != nil { liveInstructions = append(liveInstructions, producingInst) } } if v2.Valid() { - producingInst := b.valueIDToInstruction[v2.ID()] + producingInst := b.instructionOfValue(v2) if producingInst != nil { liveInstructions = append(liveInstructions, producingInst) } } if v3.Valid() { - producingInst := b.valueIDToInstruction[v3.ID()] + producingInst := b.instructionOfValue(v3) if producingInst != nil { liveInstructions = append(liveInstructions, producingInst) } } for _, v := range vs { - producingInst := b.valueIDToInstruction[v.ID()] + producingInst := b.instructionOfValue(v) if producingInst != nil { liveInstructions = append(liveInstructions, producingInst) } @@ -367,29 +356,13 @@ func (b *builder) incRefCount(id ValueID, from *Instruction) { // passNopInstElimination eliminates the instructions which is essentially a no-op. func passNopInstElimination(b *builder) { - if int(b.nextValueID) >= len(b.valueIDToInstruction) { - b.valueIDToInstruction = append(b.valueIDToInstruction, make([]*Instruction, int(b.nextValueID)-len(b.valueIDToInstruction)+1)...) - } - - for blk := b.blockIteratorBegin(); blk != nil; blk = b.blockIteratorNext() { - for cur := blk.rootInstr; cur != nil; cur = cur.next { - r1, rs := cur.Returns() - if r1.Valid() { - b.valueIDToInstruction[r1.ID()] = cur - } - for _, r := range rs { - b.valueIDToInstruction[r.ID()] = cur - } - } - } - for blk := b.blockIteratorBegin(); blk != nil; blk = b.blockIteratorNext() { for cur := blk.rootInstr; cur != nil; cur = cur.next { switch cur.Opcode() { // TODO: add more logics here. case OpcodeIshl, OpcodeSshr, OpcodeUshr: x, amount := cur.Arg2() - definingInst := b.valueIDToInstruction[amount.ID()] + definingInst := b.instructionOfValue(amount) if definingInst == nil { // If there's no defining instruction, that means the amount is coming from the parameter. continue diff --git a/internal/engine/wazevo/ssa/vs.go b/internal/engine/wazevo/ssa/vs.go index 2fd12cc65e..d906e7e354 100644 --- a/internal/engine/wazevo/ssa/vs.go +++ b/internal/engine/wazevo/ssa/vs.go @@ -38,7 +38,8 @@ func (v Variable) getType() Type { // Value represents an SSA value with a type information. The relationship with Variable is 1: N (including 0), // that means there might be multiple Variable(s) for a Value. // -// Higher 32-bit is used to store Type for this value. +// 32 to 59-bit is used to store the unique identifier of the Instruction that generates this value if any. +// 60 to 63-bit is used to store Type for this value. type Value uint64 // ValueID is the lower 32bit of Value, which is the pure identifier of Value without type info. @@ -80,7 +81,7 @@ func (v Value) Valid() bool { // Type returns the Type of this value. func (v Value) Type() Type { - return Type(v >> 32) + return Type(v >> 60) } // ID returns the valueID of this value. @@ -90,7 +91,20 @@ func (v Value) ID() ValueID { // setType sets a type to this Value and returns the updated Value. func (v Value) setType(typ Type) Value { - return v | Value(typ)<<32 + return v | Value(typ)<<60 +} + +// setInstructionID sets an Instruction.id to this Value and returns the updated Value. +func (v Value) setInstructionID(id int) Value { + if id < 0 || uint(id) >= 1<<28 { + panic(fmt.Sprintf("Too large instruction ID: %d", id)) + } + return v | Value(id)<<32 +} + +// instructionID() returns the Instruction.id of this Value. +func (v Value) instructionID() int { + return int(v>>32) & 0x0fffffff } // Values is a slice of Value. Use this instead of []Value to reuse the underlying memory. diff --git a/internal/engine/wazevo/ssa/vs_test.go b/internal/engine/wazevo/ssa/vs_test.go new file mode 100644 index 0000000000..322b1d9103 --- /dev/null +++ b/internal/engine/wazevo/ssa/vs_test.go @@ -0,0 +1,14 @@ +package ssa + +import ( + "testing" + + "github.com/tetratelabs/wazero/internal/testing/require" +) + +func TestValue_InstructionID(t *testing.T) { + v := Value(1234).setType(TypeI32).setInstructionID(5678) + require.Equal(t, ValueID(1234), v.ID()) + require.Equal(t, 5678, v.instructionID()) + require.Equal(t, TypeI32, v.Type()) +}