From a47483608739de1aded0b1e4215d258b0174a80b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 16 Dec 2024 14:57:24 -0800 Subject: [PATCH 1/2] document effects on value stack and control stack --- bbq/opcode/gen/instructions.schema.json | 100 ++++++++- bbq/opcode/instructions.yml | 256 +++++++++++++++++++++++- 2 files changed, 349 insertions(+), 7 deletions(-) diff --git a/bbq/opcode/gen/instructions.schema.json b/bbq/opcode/gen/instructions.schema.json index e0f6bda38..62c8bd968 100644 --- a/bbq/opcode/gen/instructions.schema.json +++ b/bbq/opcode/gen/instructions.schema.json @@ -5,12 +5,14 @@ "instruction": { "type": "object", "properties": { - "name": { "type": "string" }, - "description": { "type": "string" }, - "operands": { - "type": "array", - "items": { "$ref": "#/$defs/operand" } - } + "name": { "type": "string" }, + "description": { "type": "string" }, + "operands": { + "type": "array", + "items": { "$ref": "#/$defs/operand" } + }, + "valueEffects": {"$ref": "#/$defs/valueEffects"}, + "controlEffects": {"$ref": "#/$defs/controlEffects"} }, "required": ["name", "description"], "additionalProperties": false @@ -36,6 +38,92 @@ }, "required": ["name", "type"], "additionalProperties": false + }, + "valueEffects": { + "type": "object", + "properties": { + "push": { + "type": "array", + "items": { "$ref": "#/$defs/valueStackOp" } + }, + "pop": { + "type": "array", + "items": { + "$ref": "#/$defs/valueStackOp" + } + } + }, + "additionalProperties": false + }, + "valueStackOp": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "type": { + "type": "string", + "enum": [ + "value", + "string", + "array", + "integer", + "bool", + "path", + "int", + "reference", + "function", + "resource", + "optional" + ] + }, + "count": { + "oneOf": [ + { "type": "integer" }, + { "type": "string" } + ] + } + }, + "required": ["name", "type"], + "additionalProperties": false + }, + "controlEffects": { + "type": "array", + "items": { "$ref": "#/$defs/controlEffect" } + }, + "controlEffect": { + "oneOf": [ + { "$ref": "#/$defs/jump" }, + { "$ref": "#/$defs/call" }, + { "$ref": "#/$defs/return" } + ] + }, + "jump": { + "type": "object", + "properties": { + "jump": { "type": "string" } + }, + "required": ["jump"], + "additionalProperties": false + }, + "call": { + "type": "object", + "properties": { + "call": { "type": "null" } + }, + "required": ["call"], + "additionalProperties": false + }, + "return": { + "type": "object", + "properties": { + "return": { + "oneOf": [ + {"type": "string"}, + {"type": "null"} + ] + } + }, + "required": ["return"], + "additionalProperties": false } } } \ No newline at end of file diff --git a/bbq/opcode/instructions.yml b/bbq/opcode/instructions.yml index 4b12cebce..b9f466bfb 100644 --- a/bbq/opcode/instructions.yml +++ b/bbq/opcode/instructions.yml @@ -9,12 +9,20 @@ operands: - name: "localIndex" type: "index" + valueEffects: + push: + - name: "value" + type: "value" - name: "setLocal" description: Sets the value of the local at the given index to the top value on the stack. operands: - name: "localIndex" type: "index" + valueEffects: + pop: + - name: "value" + type: "value" # Global instructions @@ -23,12 +31,20 @@ operands: - name: "globalIndex" type: "index" + valueEffects: + push: + - name: "value" + type: "value" - name: "setGlobal" description: Sets the value of the global at the given index to the top value on the stack. operands: - name: "globalIndex" type: "index" + valueEffects: + pop: + - name: "value" + type: "value" # Field instructions @@ -37,31 +53,73 @@ operands: - name: "fieldNameIndex" type: "index" + valueEffects: + pop: + - name: "container" + type: "value" + push: + - name: "value" + type: "value" - name: "setField" description: Sets the value of the field at the given index to the top value on the stack. operands: - name: "fieldNameIndex" type: "index" + valueEffects: + pop: + - name: "container" + type: "value" + - name: "value" + type: "value" # Index instructions - name: "getIndex" description: Pushes the value at the given index onto the stack. + valueEffects: + pop: + - name: "array" + type: "array" + - name: "index" + type: "integer" + push: + - name: "value" + type: "value" - name: "setIndex" description: Sets the value at the given index to the top value on the stack. + valueEffects: + pop: + - name: "array" + type: "array" + - name: "index" + type: "integer" + - name: "value" + type: "value" # Value instantiation instructions - name: "true" description: Pushes the boolean value `true` onto the stack. + valueEffects: + push: + - name: "value" + type: "bool" - name: "false" description: Pushes the boolean value `false` onto the stack. + valueEffects: + push: + - name: "value" + type: "bool" - name: "nil" description: Pushes the value `nil` onto the stack. + valueEffects: + push: + - name: "value" + type: "value" - name: "path" description: Pushes the path with the given domain and identifier onto the stack. @@ -70,6 +128,10 @@ type: "pathDomain" - name: "identifierIndex" type: "index" + valueEffects: + push: + - name: "value" + type: "path" - name: "new" description: Creates a new instance of the given type. @@ -78,6 +140,10 @@ type: "compositeKind" - name: "typeIndex" type: "index" + valueEffects: + push: + - name: "value" + type: "value" - name: "newArray" description: Creates a new array with the given type and size. @@ -88,18 +154,38 @@ type: "size" - name: "isResource" type: "bool" + valueEffects: + pop: + - name: "elements" + type: "value" + # The number of elements taken from the stack is equal to the size operand of the opcode. + count: "size" + push: + - name: "array" + type: "array" - name: "newRef" description: Creates a new reference with the given type. operands: - name: "typeIndex" type: "index" + valueEffects: + pop: + - name: "value" + type: "value" + push: + - name: "reference" + type: "reference" - name: "getConstant" description: Pushes the constant at the given index onto the stack. operands: - name: "constantIndex" type: "index" + valueEffects: + push: + - name: "value" + type: "value" # Invocation instructions @@ -108,7 +194,17 @@ operands: - name: "typeArgs" type: "indices" - + valueEffects: + pop: + - name: "arguments" + # TODO: count + - name: "function" + type: "function" + push: + - name: "result" + type: "value" + controlEffects: + - call: - name: "invokeDynamic" description: Invokes the dynamic function with the given name, type arguments, and argument count. @@ -119,24 +215,57 @@ type: "indices" - name: "argCount" type: "size" + valueEffects: + pop: + - name: "arguments" + # TODO: count + push: + - name: "result" + type: "value" + controlEffects: + - call: # Value stack instructions - name: "dup" description: Duplicates the top value on the stack. + valueEffects: + pop: + - name: "value" + type: "value" + push: + - name: "original" + type: "value" + - name: "duplicate" + type: "value" - name: "drop" description: Removes the top value from the stack. + valueEffects: + pop: + - name: "value" + type: "value" # Resource stack instructions - name: "destroy" description: Destroys the top value on the stack. + valueEffects: + pop: + - name: "resource" + type: "resource" # Optional instructions - name: "unwrap" description: Unwraps the top value on the stack. + valueEffects: + pop: + - name: "optional" + type: "optional" + push: + - name: "value" + type: "value" # Conversion instructions @@ -145,6 +274,13 @@ operands: - name: "typeIndex" type: "index" + valueEffects: + pop: + - name: "value" + type: "value" + push: + - name: "value" + type: "value" - name: "cast" description: Casts the top value on the stack to the given type. @@ -153,6 +289,13 @@ type: "index" - name: "kind" type: "castKind" + valueEffects: + pop: + - name: "value" + type: "value" + push: + - name: "value" + type: "value" # Control flow instructions @@ -161,54 +304,165 @@ operands: - name: "target" type: "index" + controlEffects: + - jump: "target" - name: "jumpIfFalse" description: Jumps to the given instruction, if the top value on the stack is `false`. operands: - name: "target" type: "index" + controlEffects: + - jump: "target" - name: "return" description: Returns from the current function, without a value. + controlEffects: + - return: - name: "returnValue" description: Returns from the current function, with the top value on the stack. + valueEffects: + pop: + - name: "value" + type: "value" + controlEffects: + - return: "value" # Comparison instructions - name: "equal" description: Compares the top two values on the stack for equality. + valueEffects: + pop: + - name: "left" + type: "value" + - name: "right" + type: "value" + push: + - name: "result" + type: "bool" - name: "notEqual" description: Compares the top two values on the stack for inequality. + valueEffects: + pop: + - name: "left" + type: "value" + - name: "right" + type: "value" + push: + - name: "result" + type: "bool" # Integer arithmetic instructions - name: "intAdd" description: Adds the top two values on the stack. + valueEffects: + pop: + - name: "left" + type: "int" + - name: "right" + type: "int" + push: + - name: "result" + type: "int" - name: "intSubtract" description: Subtracts the top two values on the stack. + valueEffects: + pop: + - name: "left" + type: "int" + - name: "right" + type: "int" + push: + - name: "result" + type: "int" - name: "intMultiply" description: Multiplies the top two values on the stack. + valueEffects: + pop: + - name: "left" + type: "int" + - name: "right" + type: "int" + push: + - name: "result" + type: "int" - name: "intDivide" description: Divides the top two values on the stack. + valueEffects: + pop: + - name: "left" + type: "int" + - name: "right" + type: "int" + push: + - name: "result" + type: "int" - name: "intMod" description: Calculates the modulo of the top two values on the stack. + valueEffects: + pop: + - name: "left" + type: "int" + - name: "right" + type: "int" + push: + - name: "result" + type: "int" # Integer comparison instructions - name: "intLess" description: Compares the top two values on the stack for less than. + valueEffects: + pop: + - name: "left" + type: "int" + - name: "right" + type: "int" + push: + - name: "result" + type: "bool" - name: "intLessOrEqual" description: Compares the top two values on the stack for less than or equal. + valueEffects: + pop: + - name: "left" + type: "int" + - name: "right" + type: "int" + push: + - name: "result" + type: "bool" - name: "intGreater" description: Compares the top two values on the stack for greater than. + valueEffects: + pop: + - name: "left" + type: "int" + - name: "right" + type: "int" + push: + - name: "result" + type: "bool" - name: "intGreaterOrEqual" description: Compares the top two values on the stack for greater than or equal. + valueEffects: + pop: + - name: "left" + type: "int" + - name: "right" + type: "int" + push: + - name: "result" + type: "bool" From d6c082c59603bae8fb3be471b2baa3af2bbf3d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Wed, 22 Jan 2025 13:49:19 -0800 Subject: [PATCH 2/2] fix tests --- bbq/opcode/print_test.go | 60 ++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/bbq/opcode/print_test.go b/bbq/opcode/print_test.go index 3b443a1a0..0f5f360d1 100644 --- a/bbq/opcode/print_test.go +++ b/bbq/opcode/print_test.go @@ -99,17 +99,17 @@ func TestPrintInstruction(t *testing.T) { "JumpIfFalse target:258": {byte(JumpIfFalse), 1, 2}, "Transfer typeIndex:258": {byte(Transfer), 1, 2}, - "New kind:258 typeIndex:772": {byte(New), 1, 2, 3, 4}, + "New kind:CompositeKind(258) typeIndex:772": {byte(New), 1, 2, 3, 4}, "Cast typeIndex:258 kind:3": {byte(Cast), 1, 2, 3}, - `Path domain:1 identifier:"hello"`: {byte(Path), 1, 0, 5, 'h', 'e', 'l', 'l', 'o'}, + `Path domain:PathDomainStorage identifierIndex:5`: {byte(Path), 1, 0, 5}, - `InvokeDynamic name:"abc" typeArgs:[772 1286] argCount:1800`: { - byte(InvokeDynamic), 0, 3, 'a', 'b', 'c', 0, 2, 3, 4, 5, 6, 7, 8, + `InvokeDynamic nameIndex:1 typeArgs:[772, 1286] argCount:1800`: { + byte(InvokeDynamic), 0, 1, 0, 2, 3, 4, 5, 6, 7, 8, }, - "Invoke typeArgs:[772 1286]": { + "Invoke typeArgs:[772, 1286]": { byte(Invoke), 0, 2, 3, 4, 5, 6, }, @@ -117,31 +117,31 @@ func TestPrintInstruction(t *testing.T) { "NewArray typeIndex:258 size:772 isResource:true": {byte(NewArray), 1, 2, 3, 4, 1}, - "Unknown": {byte(Unknown)}, - "Return": {byte(Return)}, - "ReturnValue": {byte(ReturnValue)}, - "IntAdd": {byte(IntAdd)}, - "IntSubtract": {byte(IntSubtract)}, - "IntMultiply": {byte(IntMultiply)}, - "IntDivide": {byte(IntDivide)}, - "IntMod": {byte(IntMod)}, - "IntLess": {byte(IntLess)}, - "IntGreater": {byte(IntGreater)}, - "IntLessOrEqual": {byte(IntLessOrEqual)}, - "IntGreaterOrEqual": {byte(IntGreaterOrEqual)}, - "Equal": {byte(Equal)}, - "NotEqual": {byte(NotEqual)}, - "Unwrap": {byte(Unwrap)}, - "Destroy": {byte(Destroy)}, - "True": {byte(True)}, - "False": {byte(False)}, - "Nil": {byte(Nil)}, - "GetField": {byte(GetField)}, - "SetField": {byte(SetField)}, - "SetIndex": {byte(SetIndex)}, - "GetIndex": {byte(GetIndex)}, - "Drop": {byte(Drop)}, - "Dup": {byte(Dup)}, + "Unknown": {byte(Unknown)}, + "Return": {byte(Return)}, + "ReturnValue": {byte(ReturnValue)}, + "IntAdd": {byte(IntAdd)}, + "IntSubtract": {byte(IntSubtract)}, + "IntMultiply": {byte(IntMultiply)}, + "IntDivide": {byte(IntDivide)}, + "IntMod": {byte(IntMod)}, + "IntLess": {byte(IntLess)}, + "IntGreater": {byte(IntGreater)}, + "IntLessOrEqual": {byte(IntLessOrEqual)}, + "IntGreaterOrEqual": {byte(IntGreaterOrEqual)}, + "Equal": {byte(Equal)}, + "NotEqual": {byte(NotEqual)}, + "Unwrap": {byte(Unwrap)}, + "Destroy": {byte(Destroy)}, + "True": {byte(True)}, + "False": {byte(False)}, + "Nil": {byte(Nil)}, + "GetField fieldNameIndex:258": {byte(GetField), 1, 2}, + "SetField fieldNameIndex:258": {byte(SetField), 1, 2}, + "SetIndex": {byte(SetIndex)}, + "GetIndex": {byte(GetIndex)}, + "Drop": {byte(Drop)}, + "Dup": {byte(Dup)}, } for expected, code := range instructions {