1package ssa
2
3import (
4 "fmt"
5 "math"
6
7 "github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi"
8)
9
10// Variable is a unique identifier for a source program's variable and will correspond to
11// multiple ssa Value(s).
12//
13// For example, `Local 1` is a Variable in WebAssembly, and Value(s) will be created for it
14// whenever it executes `local.set 1`.
15//
16// Variable is useful to track the SSA Values of a variable in the source program, and
17// can be used to find the corresponding latest SSA Value via Builder.FindValue.
18//
19// Higher 4-bit is used to store Type for this variable.
20type Variable uint32
21
22// String implements fmt.Stringer.
23func (v Variable) String() string {
24 return fmt.Sprintf("var%d", v&0x0fffffff)
25}
26
27func (v Variable) setType(typ Type) Variable {
28 if v >= 1<<28 {
29 panic(fmt.Sprintf("Too large variable: %d", v))
30 }
31 return Variable(typ)<<28 | v
32}
33
34func (v Variable) getType() Type {
35 return Type(v >> 28)
36}
37
38// Value represents an SSA value with a type information. The relationship with Variable is 1: N (including 0),
39// that means there might be multiple Variable(s) for a Value.
40//
41// 32 to 59-bit is used to store the unique identifier of the Instruction that generates this value if any.
42// 60 to 63-bit is used to store Type for this value.
43type Value uint64
44
45// ValueID is the lower 32bit of Value, which is the pure identifier of Value without type info.
46type ValueID uint32
47
48const (
49 valueIDInvalid ValueID = math.MaxUint32
50 ValueInvalid = Value(valueIDInvalid)
51)
52
53// Format creates a debug string for this Value using the data stored in Builder.
54func (v Value) Format(b Builder) string {
55 if annotation, ok := b.(*builder).valueAnnotations[v.ID()]; ok {
56 return annotation
57 }
58 return fmt.Sprintf("v%d", v.ID())
59}
60
61func (v Value) formatWithType(b Builder) (ret string) {
62 if annotation, ok := b.(*builder).valueAnnotations[v.ID()]; ok {
63 ret = annotation + ":" + v.Type().String()
64 } else {
65 ret = fmt.Sprintf("v%d:%s", v.ID(), v.Type())
66 }
67
68 if wazevoapi.SSALoggingEnabled { // This is useful to check live value analysis bugs.
69 if bd := b.(*builder); bd.donePostBlockLayoutPasses {
70 id := v.ID()
71 ret += fmt.Sprintf("(ref=%d)", bd.valuesInfo[id].RefCount)
72 }
73 }
74 return ret
75}
76
77// Valid returns true if this value is valid.
78func (v Value) Valid() bool {
79 return v.ID() != valueIDInvalid
80}
81
82// Type returns the Type of this value.
83func (v Value) Type() Type {
84 return Type(v >> 60)
85}
86
87// ID returns the valueID of this value.
88func (v Value) ID() ValueID {
89 return ValueID(v)
90}
91
92// setType sets a type to this Value and returns the updated Value.
93func (v Value) setType(typ Type) Value {
94 return v | Value(typ)<<60
95}
96
97// setInstructionID sets an Instruction.id to this Value and returns the updated Value.
98func (v Value) setInstructionID(id int) Value {
99 if id < 0 || uint(id) >= 1<<28 {
100 panic(fmt.Sprintf("Too large instruction ID: %d", id))
101 }
102 return v | Value(id)<<32
103}
104
105// instructionID() returns the Instruction.id of this Value.
106func (v Value) instructionID() int {
107 return int(v>>32) & 0x0fffffff
108}
109
110// Values is a slice of Value. Use this instead of []Value to reuse the underlying memory.
111type Values = wazevoapi.VarLength[Value]
112
113// ValuesNil is a nil Values.
114var ValuesNil = wazevoapi.NewNilVarLength[Value]()