Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ssa: removes valueIDToInstruction slice #2286

Merged
merged 2 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions internal/engine/wazevo/ssa/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
Expand Down Expand Up @@ -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)
}
37 changes: 5 additions & 32 deletions internal/engine/wazevo/ssa/pass.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
}
}
}

Expand All @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down
20 changes: 17 additions & 3 deletions internal/engine/wazevo/ssa/vs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
14 changes: 14 additions & 0 deletions internal/engine/wazevo/ssa/vs_test.go
Original file line number Diff line number Diff line change
@@ -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())
}
Loading