From 0999f2860116ced7635c739c5eb227c9e199e7f0 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Fri, 9 May 2025 18:47:20 +0300 Subject: [PATCH 01/33] Refactored base instruction types --- aarch64/codegen/instruction.go | 10 +++--- aarch64/isa/base.go | 12 ------- gen/function_generator.go | 4 +-- gen/instruction_definition.go | 22 +++++++----- gen/instruction_generator.go | 5 +-- gen/instruction_generator_test.go | 60 ++++++++++++++++++------------- gen/instruction_info.go | 6 ++-- go.sum | 2 -- usm64/ssa/ssa_construction.go | 2 +- 9 files changed, 63 insertions(+), 60 deletions(-) delete mode 100644 aarch64/isa/base.go diff --git a/aarch64/codegen/instruction.go b/aarch64/codegen/instruction.go index 3eefb42..e364c69 100644 --- a/aarch64/codegen/instruction.go +++ b/aarch64/codegen/instruction.go @@ -11,12 +11,12 @@ import ( ) type Instruction interface { - gen.BaseInstruction + gen.InstructionDefinition // Converts the abstract instruction representation into a concrete binary // instruction. - Generate( - *InstructionCodegenContext, + Codegen( + ctx *InstructionCodegenContext, ) (instructions.Instruction, core.ResultList) } @@ -34,7 +34,7 @@ func (ctx *InstructionCodegenContext) InstructionOffsetInFile() uint64 { func (ctx *InstructionCodegenContext) Codegen( buffer *bytes.Buffer, ) core.ResultList { - instruction, ok := ctx.Instruction.(Instruction) + instruction, ok := ctx.InstructionInfo.Instruction.(Instruction) if !ok { return list.FromSingle(core.Result{ { @@ -45,7 +45,7 @@ func (ctx *InstructionCodegenContext) Codegen( }) } - binaryInst, results := instruction.Generate(ctx) + binaryInst, results := instruction.Codegen(ctx) if !results.IsEmpty() { return results } diff --git a/aarch64/isa/base.go b/aarch64/isa/base.go deleted file mode 100644 index 33d20d4..0000000 --- a/aarch64/isa/base.go +++ /dev/null @@ -1,12 +0,0 @@ -package aarch64isa - -import ( - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" -) - -type NonBranchingInstruction struct{} - -func (NonBranchingInstruction) PossibleNextSteps() (gen.StepInfo, core.ResultList) { - return gen.StepInfo{PossibleContinue: true}, core.ResultList{} -} diff --git a/gen/function_generator.go b/gen/function_generator.go index f9f3592..3311865 100644 --- a/gen/function_generator.go +++ b/gen/function_generator.go @@ -144,7 +144,7 @@ func (g *FunctionGenerator) getInstructionBranchingDestinations( info *InstructionInfo, labels functionLabelData, ) ([]int, core.ResultList) { - steps, results := info.Instruction.PossibleNextSteps() + steps, results := info.Instruction.PossibleNextSteps(info) if !results.IsEmpty() { return nil, results } @@ -286,7 +286,7 @@ func (g *FunctionGenerator) generateBasicBlocks( basicBlockLength := len(currentBasicBlock.Instructions) lastInstruction := currentBasicBlock.Instructions[basicBlockLength-1] - steps, results := lastInstruction.Instruction.PossibleNextSteps() + steps, results := lastInstruction.Instruction.PossibleNextSteps(lastInstruction) if !results.IsEmpty() { return results } diff --git a/gen/instruction_definition.go b/gen/instruction_definition.go index 94a62d2..b68ab2f 100644 --- a/gen/instruction_definition.go +++ b/gen/instruction_definition.go @@ -4,27 +4,31 @@ import ( "alon.kr/x/usm/core" ) -type BaseInstruction interface { +type InstructionDefinition interface { // This method is usd by the USM engine to generate the internal control // flow graph representation. // // Should return a non-empty slice. If the instruction does not have any // consecutive steps in the function (for example, a return statement), // then a special dedicated return step should be returned. - PossibleNextSteps() (StepInfo, core.ResultList) + PossibleNextSteps(*InstructionInfo) (StepInfo, core.ResultList) // Returns the string that represents the operator of the instruction. // For example, for the add instruction this method would return "ADD". // // This is required because some instructions may be generated automatically, // and we want to be able to display them in a human-readable format. - Operator() string + Operator(*InstructionInfo) string + + // Validate the instruction information structure, according to the + // expected arguments, targets, and other related information. + Validate(*InstructionInfo) core.ResultList } -// A basic instruction definition. This defines the logic that converts the -// generic, architecture / instruction set independent instruction AST nodes -// into a format instruction which is part of a specific instruction set. -type InstructionDefinition interface { - // Build an instruction from the provided instruction information. - BuildInstruction(info *InstructionInfo) (BaseInstruction, core.ResultList) +type NonBranchingInstruction struct{} + +func (NonBranchingInstruction) PossibleNextSteps(*InstructionInfo) (StepInfo, core.ResultList) { + return StepInfo{ + PossibleContinue: true, + }, core.ResultList{} } diff --git a/gen/instruction_generator.go b/gen/instruction_generator.go index 04bd526..92b1872 100644 --- a/gen/instruction_generator.go +++ b/gen/instruction_generator.go @@ -137,11 +137,12 @@ func (g *InstructionGenerator) Generate( instCtx.InstructionInfo.AppendTarget(targets...) instCtx.InstructionInfo.AppendArgument(arguments...) - instruction, results := instDef.BuildInstruction(instCtx.InstructionInfo) + instCtx.InstructionInfo.SetInstruction(instDef) + + results = instDef.Validate(instCtx.InstructionInfo) if !results.IsEmpty() { return nil, results } - instCtx.InstructionInfo.Instruction = instruction return instCtx.InstructionInfo, core.ResultList{} } diff --git a/gen/instruction_generator_test.go b/gen/instruction_generator_test.go index c3c2a60..506688a 100644 --- a/gen/instruction_generator_test.go +++ b/gen/instruction_generator_test.go @@ -16,20 +16,24 @@ import ( type AddInstruction struct{} -func (i *AddInstruction) PossibleNextSteps() (gen.StepInfo, core.ResultList) { +func (AddInstruction) PossibleNextSteps(*gen.InstructionInfo) (gen.StepInfo, core.ResultList) { return gen.StepInfo{PossibleContinue: true}, core.ResultList{} } -func (i *AddInstruction) Operator() string { +func (AddInstruction) Operator(*gen.InstructionInfo) string { return "ADD" } +func (AddInstruction) Validate(info *gen.InstructionInfo) core.ResultList { + return core.ResultList{} +} + type AddInstructionDefinition struct{} func (AddInstructionDefinition) BuildInstruction( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - return gen.BaseInstruction(&AddInstruction{}), core.ResultList{} +) (gen.InstructionDefinition, core.ResultList) { + return gen.InstructionDefinition(AddInstruction{}), core.ResultList{} } func (AddInstructionDefinition) InferTargetTypes( @@ -71,20 +75,24 @@ func (AddInstructionDefinition) InferTargetTypes( type RetInstruction struct{} -func (i *RetInstruction) PossibleNextSteps() (gen.StepInfo, core.ResultList) { +func (RetInstruction) PossibleNextSteps(*gen.InstructionInfo) (gen.StepInfo, core.ResultList) { return gen.StepInfo{PossibleReturn: true}, core.ResultList{} } -func (i *RetInstruction) Operator() string { +func (RetInstruction) Operator(*gen.InstructionInfo) string { return "RET" } +func (RetInstruction) Validate(*gen.InstructionInfo) core.ResultList { + return core.ResultList{} +} + type RetInstructionDefinition struct{} func (RetInstructionDefinition) BuildInstruction( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - return gen.BaseInstruction(&RetInstruction{}), core.ResultList{} +) (gen.InstructionDefinition, core.ResultList) { + return gen.InstructionDefinition(RetInstruction{}), core.ResultList{} } func (RetInstructionDefinition) InferTargetTypes( @@ -97,11 +105,9 @@ func (RetInstructionDefinition) InferTargetTypes( // MARK: Jump -type JumpInstruction struct { - *gen.InstructionInfo -} +type JumpInstruction struct{} -func (i *JumpInstruction) PossibleNextSteps() (gen.StepInfo, core.ResultList) { +func (JumpInstruction) PossibleNextSteps(i *gen.InstructionInfo) (gen.StepInfo, core.ResultList) { return gen.StepInfo{ PossibleBranches: []*gen.LabelInfo{ i.Arguments[0].(*gen.LabelArgumentInfo).Label, @@ -109,16 +115,20 @@ func (i *JumpInstruction) PossibleNextSteps() (gen.StepInfo, core.ResultList) { }, core.ResultList{} } -func (i *JumpInstruction) Operator() string { +func (JumpInstruction) Operator(*gen.InstructionInfo) string { return "JMP" } +func (JumpInstruction) Validate(info *gen.InstructionInfo) core.ResultList { + return core.ResultList{} +} + type JumpInstructionDefinition struct{} func (JumpInstructionDefinition) BuildInstruction( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - return gen.BaseInstruction(&JumpInstruction{info}), core.ResultList{} +) (gen.InstructionDefinition, core.ResultList) { + return gen.InstructionDefinition(JumpInstruction{}), core.ResultList{} } func (JumpInstructionDefinition) InferTargetTypes( @@ -132,11 +142,9 @@ func (JumpInstructionDefinition) InferTargetTypes( // MARK: Jump Zero // JZ %condition .label -type JumpZeroInstruction struct { - *gen.InstructionInfo -} +type JumpZeroInstruction struct{} -func (i *JumpZeroInstruction) PossibleNextSteps() (gen.StepInfo, core.ResultList) { +func (JumpZeroInstruction) PossibleNextSteps(i *gen.InstructionInfo) (gen.StepInfo, core.ResultList) { label := i.Arguments[1].(*gen.LabelArgumentInfo).Label return gen.StepInfo{ PossibleBranches: []*gen.LabelInfo{label}, @@ -144,16 +152,20 @@ func (i *JumpZeroInstruction) PossibleNextSteps() (gen.StepInfo, core.ResultList }, core.ResultList{} } -func (i *JumpZeroInstruction) Operator() string { +func (JumpZeroInstruction) Operator(*gen.InstructionInfo) string { return "JZ" } +func (JumpZeroInstruction) Validate(info *gen.InstructionInfo) core.ResultList { + return core.ResultList{} +} + type JumpZeroInstructionDefinition struct{} func (JumpZeroInstructionDefinition) BuildInstruction( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - return gen.BaseInstruction(&JumpZeroInstruction{info}), core.ResultList{} +) (gen.InstructionDefinition, core.ResultList) { + return gen.InstructionDefinition(JumpZeroInstruction{}), core.ResultList{} } func (JumpZeroInstructionDefinition) InferTargetTypes( @@ -172,7 +184,7 @@ func (m *InstructionMap) GetInstructionDefinition( name string, node parse.InstructionNode, ) (gen.InstructionDefinition, core.ResultList) { - instDef, ok := (*m)[name] + inst, ok := (*m)[name] if !ok { return nil, list.FromSingle(core.Result{{ Type: core.ErrorResult, @@ -180,7 +192,7 @@ func (m *InstructionMap) GetInstructionDefinition( Location: &node.Operator, }}) } - return instDef, core.ResultList{} + return inst, core.ResultList{} } func PrepareTestForInstructionGeneration( diff --git a/gen/instruction_info.go b/gen/instruction_info.go index 9704983..01aeace 100644 --- a/gen/instruction_info.go +++ b/gen/instruction_info.go @@ -13,7 +13,7 @@ type InstructionInfo struct { Arguments []ArgumentInfo // The actual instruction information, which is ISA specific. - Instruction BaseInstruction + Instruction InstructionDefinition // The location in which the instruction was defined in the source code. // Can be nil if the instruction was defined internally, for example, @@ -61,13 +61,13 @@ func (i *InstructionInfo) AppendArgument(arguments ...ArgumentInfo) { // This can be used to update the instruction, but keep the same arguments and // targets, for example, as an optimization to a more specific operation which // accepts the same arguments in certain cases. -func (i *InstructionInfo) SetBaseInstruction(instruction BaseInstruction) { +func (i *InstructionInfo) SetInstruction(instruction InstructionDefinition) { i.Instruction = instruction } func (i *InstructionInfo) String() string { s := "" - operator := i.Instruction.Operator() + operator := i.Instruction.Operator(i) if len(i.Targets) > 0 { for _, target := range i.Targets { diff --git a/go.sum b/go.sum index d07f0f9..3c413ef 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ alon.kr/x/aarch64codegen v0.0.0-20250423211537-52c2f85d1367 h1:l+WdERkNIO4GVBHvcqs0PJSELEkr3A0CGbwSTL3/6pE= alon.kr/x/aarch64codegen v0.0.0-20250423211537-52c2f85d1367/go.mod h1:WAdZYqOdp9KwoBjWamrMDAphahR5oXkPSICj3+tLIyQ= -alon.kr/x/faststringmap v0.0.0-20250425112818-35d6525968e3 h1:i2q4NANxEeSzGiWidH8lodMM/fZ3eCxfZdvA4WDKC9I= -alon.kr/x/faststringmap v0.0.0-20250425112818-35d6525968e3/go.mod h1:dPORoTvAFIehHRaI/lfJV3eT6PL5tY7fpAtxzz7rQVw= alon.kr/x/faststringmap v0.0.0-20250503134653-20d6364c2c94 h1:EdG6bQh5IT5MZ76MY85/GK4Ma7zoJ4zcFqd2fpxK6NI= alon.kr/x/faststringmap v0.0.0-20250503134653-20d6364c2c94/go.mod h1:dPORoTvAFIehHRaI/lfJV3eT6PL5tY7fpAtxzz7rQVw= alon.kr/x/graph v0.0.0-20250319212444-dd67d0281ab7 h1:0d/oLvPQWc1B38ZfWfJ5UQXeHx9JT8gmp3K0AOOG8aQ= diff --git a/usm64/ssa/ssa_construction.go b/usm64/ssa/ssa_construction.go index 4eee9bc..f4b4172 100644 --- a/usm64/ssa/ssa_construction.go +++ b/usm64/ssa/ssa_construction.go @@ -29,7 +29,7 @@ func (s *ConstructionScheme) NewPhiInstruction( target := gen.NewTargetInfo(register) info.AppendTarget(&target) instruction, results := usm64isa.NewPhiInstruction(info) - info.SetBaseInstruction(instruction) + info.SetInstruction(instruction) block.PrependInstruction(info) return ssa.PhiInstruction(instruction), results } From 43e7457ca868eac7b887ed9b8c9fca833e6277c4 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Fri, 9 May 2025 18:48:25 +0300 Subject: [PATCH 02/33] Fixed AArch64 `adds` definition to use new instruction API --- aarch64/isa/adds.go | 64 +++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 46 deletions(-) diff --git a/aarch64/isa/adds.go b/aarch64/isa/adds.go index a3e50ff..2dde0d1 100644 --- a/aarch64/isa/adds.go +++ b/aarch64/isa/adds.go @@ -9,67 +9,43 @@ import ( "alon.kr/x/usm/gen" ) -type BaseAdds struct { - NonBranchingInstruction +type Adds struct { + gen.NonBranchingInstruction } -func (BaseAdds) Operator() string { +func (Adds) Operator() string { return "adds" } -type AddsReg struct { - BaseAdd - instructions.AddShiftedRegister -} - -func (i AddsReg) Generate( - *aarch64codegen.InstructionCodegenContext, -) (instructions.Instruction, core.ResultList) { - return i, core.ResultList{} -} - -type AddsImm struct { - BaseAdd - instructions.AddsImmediate -} - -func (i AddsImm) Generate( - *aarch64codegen.InstructionCodegenContext, -) (instructions.Instruction, core.ResultList) { - return i, core.ResultList{} -} - -type AddsDefinition struct{} - -func (d AddsDefinition) buildRegisterVariant( +func (adds Adds) codegenRegisterVariant( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +) (instructions.Instruction, core.ResultList) { Xd, Xn, Xm, results := aarch64translation.BinaryInstructionToAarch64(info) if !results.IsEmpty() { return nil, results } - return AddReg{ - AddShiftedRegister: instructions.NewAddsShiftedRegister(Xd, Xn, Xm), - }, core.ResultList{} + inst := instructions.NewAddShiftedRegister(Xd, Xn, Xm) + return inst, core.ResultList{} } -func (AddsDefinition) buildImmediateVariant( +func (adds Adds) codegenImmediateVariant( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +) (instructions.Instruction, core.ResultList) { Xd, Xn, imm, results := aarch64translation.Immediate12GPRegisterTargetInstructionToAarch64(info) if !results.IsEmpty() { return nil, results } - return AddsImm{ - AddsImmediate: instructions.NewAddsImmediate(Xd, Xn, imm), - }, core.ResultList{} + inst := instructions.NewAddsImmediate(Xd, Xn, imm) + return inst, core.ResultList{} } -func (d AddsDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +func (adds Adds) Codegen( + ctx *aarch64codegen.InstructionCodegenContext, +) (instructions.Instruction, core.ResultList) { + info := ctx.InstructionInfo + results := aarch64translation.ValidateBinaryInstruction(info) if !results.IsEmpty() { return nil, results @@ -77,10 +53,10 @@ func (d AddsDefinition) BuildInstruction( switch info.Arguments[1].(type) { case *gen.RegisterArgumentInfo: - return d.buildRegisterVariant(info) + return adds.codegenRegisterVariant(info) case *gen.ImmediateInfo: - return d.buildImmediateVariant(info) + return adds.codegenImmediateVariant(info) default: return nil, list.FromSingle(core.Result{ @@ -92,7 +68,3 @@ func (d AddsDefinition) BuildInstruction( }) } } - -func NewAddsInstructionDefinition() gen.InstructionDefinition { - return AddsDefinition{} -} From 27dfe5c935bee5d1d9f435193affac2a58794e81 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Fri, 9 May 2025 19:00:42 +0300 Subject: [PATCH 03/33] (Hopefully) fixed AArch64 `add` and `adds` tests --- aarch64/isa/add.go | 77 +++++++++++++++++------------------------ aarch64/isa/add_test.go | 14 +++----- aarch64/isa/adds.go | 13 ++++++- 3 files changed, 49 insertions(+), 55 deletions(-) diff --git a/aarch64/isa/add.go b/aarch64/isa/add.go index 448e49b..140836c 100644 --- a/aarch64/isa/add.go +++ b/aarch64/isa/add.go @@ -9,67 +9,49 @@ import ( "alon.kr/x/usm/gen" ) -type BaseAdd struct { - NonBranchingInstruction +type Add struct { + gen.NonBranchingInstruction } -func (BaseAdd) Operator() string { - return "add" -} - -type AddReg struct { - BaseAdd - instructions.AddShiftedRegister -} - -func (i AddReg) Generate( - *aarch64codegen.InstructionCodegenContext, -) (instructions.Instruction, core.ResultList) { - return i, core.ResultList{} -} - -type AddImm struct { - BaseAdd - instructions.AddImmediate +func NewAddInstruction() gen.InstructionDefinition { + return Add{} } -func (i AddImm) Generate( - *aarch64codegen.InstructionCodegenContext, -) (instructions.Instruction, core.ResultList) { - return i, core.ResultList{} +func (Add) Operator(*gen.InstructionInfo) string { + return "add" } -type AddDefinition struct{} - -func (d AddDefinition) buildRegisterVariant( +func (add Add) codegenRegisterVariant( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +) (instructions.Instruction, core.ResultList) { Xd, Xn, Xm, results := aarch64translation.BinaryInstructionToAarch64(info) if !results.IsEmpty() { return nil, results } - return AddReg{ - AddShiftedRegister: instructions.NewAddShiftedRegister(Xd, Xn, Xm), - }, core.ResultList{} + inst := instructions.NewAddShiftedRegister(Xd, Xn, Xm) + return inst, core.ResultList{} } -func (AddDefinition) buildImmediateVariant( +func (add Add) codegenImmediateVariant( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +) (instructions.Instruction, core.ResultList) { Xd, Xn, imm, results := aarch64translation.Immediate12InstructionToAarch64(info) if !results.IsEmpty() { return nil, results } - return AddImm{ - AddImmediate: instructions.NewAddImmediate(Xd, Xn, imm), - }, core.ResultList{} + inst := instructions.NewAddImmediate(Xd, Xn, imm) + return inst, core.ResultList{} } -func (d AddDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +func (add Add) Codegen( + ctx *aarch64codegen.InstructionCodegenContext, +) (instructions.Instruction, core.ResultList) { + // TODO: this implementation is very similar to the one in adds.go, and possibly + // other binary arithmetic instructions. Consider refactoring this. + + info := ctx.InstructionInfo results := aarch64translation.ValidateBinaryInstruction(info) if !results.IsEmpty() { return nil, results @@ -77,11 +59,9 @@ func (d AddDefinition) BuildInstruction( switch info.Arguments[1].(type) { case *gen.RegisterArgumentInfo: - return d.buildRegisterVariant(info) - + return add.codegenRegisterVariant(info) case *gen.ImmediateInfo: - return d.buildImmediateVariant(info) - + return add.codegenImmediateVariant(info) default: return nil, list.FromSingle(core.Result{ { @@ -93,6 +73,13 @@ func (d AddDefinition) BuildInstruction( } } -func NewAddInstructionDefinition() gen.InstructionDefinition { - return AddDefinition{} +func (add Add) Validate( + info *gen.InstructionInfo, +) core.ResultList { + // TODO: this is a pretty hacky way to validate the instruction: we create + // a "mock" generation context, and then try to generate the binary + // representation of the instruction. + ctx := aarch64codegen.InstructionCodegenContext{InstructionInfo: info} + _, results := add.Codegen(&ctx) + return results } diff --git a/aarch64/isa/add_test.go b/aarch64/isa/add_test.go index 9cc621c..704eed3 100644 --- a/aarch64/isa/add_test.go +++ b/aarch64/isa/add_test.go @@ -39,15 +39,11 @@ func buildInstructionFromSource( NewFunctionGenerationContext() generator := gen.NewInstructionGenerator() - baseInfo, results := generator.Generate(ctx, node) + info, results := generator.Generate(ctx, node) assert.True(t, results.IsEmpty()) - assert.NotNil(t, baseInfo) + assert.NotNil(t, info) - baseInst, results := def.BuildInstruction(baseInfo) - assert.True(t, results.IsEmpty()) - assert.NotNil(t, baseInst) - - inst, ok := baseInst.(aarch64codegen.Instruction) + inst, ok := info.Instruction.(aarch64codegen.Instruction) assert.True(t, ok) return inst @@ -62,14 +58,14 @@ func assertExpectedCodegen( inst := buildInstructionFromSource(t, def, src) generationContext := &aarch64codegen.InstructionCodegenContext{} - code, results := inst.Generate(generationContext) + code, results := inst.Codegen(generationContext) assert.True(t, results.IsEmpty()) assert.Equal(t, expected.Binary(), code.Binary()) } func TestAddExpectedCodegen(t *testing.T) { - def := aarch64isa.NewAddInstructionDefinition() + def := aarch64isa.NewAddInstruction() testCases := []struct { src string diff --git a/aarch64/isa/adds.go b/aarch64/isa/adds.go index 2dde0d1..cfa53df 100644 --- a/aarch64/isa/adds.go +++ b/aarch64/isa/adds.go @@ -25,7 +25,7 @@ func (adds Adds) codegenRegisterVariant( return nil, results } - inst := instructions.NewAddShiftedRegister(Xd, Xn, Xm) + inst := instructions.NewAddsShiftedRegister(Xd, Xn, Xm) return inst, core.ResultList{} } @@ -68,3 +68,14 @@ func (adds Adds) Codegen( }) } } + +func (adds Adds) Validate( + info *gen.InstructionInfo, +) core.ResultList { + // TODO: this is a pretty hacky way to validate the instruction: we create + // a "mock" generation context, and then try to generate the binary + // representation of the instruction. + ctx := aarch64codegen.InstructionCodegenContext{InstructionInfo: info} + _, results := adds.Codegen(&ctx) + return results +} From 4b46a3850048709d24a733f39b270546304c6aec Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Fri, 9 May 2025 19:11:31 +0300 Subject: [PATCH 04/33] Updated `b` AArch64 instruction to new instruction API --- aarch64/isa/b.go | 90 ++++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/aarch64/isa/b.go b/aarch64/isa/b.go index 350d0e7..68172fb 100644 --- a/aarch64/isa/b.go +++ b/aarch64/isa/b.go @@ -9,53 +9,47 @@ import ( "alon.kr/x/usm/gen" ) -type Branch struct { - Target *gen.LabelInfo +type Branch struct{} + +func NewBranch() Branch { + return Branch{} } func (b Branch) Operator() string { return "b" } -func (b Branch) PossibleNextSteps() (gen.StepInfo, core.ResultList) { - return gen.StepInfo{ - PossibleBranches: []*gen.LabelInfo{b.Target}, - }, core.ResultList{} -} - -func (b Branch) Generate( - ctx *aarch64codegen.InstructionCodegenContext, -) (instructions.Instruction, core.ResultList) { - targetBasicBlock := b.Target.BasicBlock - targetOffset := ctx.BasicBlockOffsets[targetBasicBlock] - currentOffset := ctx.InstructionOffsetInFunction - offset, err := aarch64translation.Uint64DiffToOffset26Align4(targetOffset, currentOffset) +func (b Branch) Target( + info *gen.InstructionInfo, +) (*gen.LabelInfo, core.ResultList) { + results := aarch64translation.AssertArgumentsExactly(info, 1) + if !results.IsEmpty() { + return nil, results + } - if err != nil { - return nil, list.FromSingle(core.Result{ - { - Type: core.ErrorResult, - Message: "Invalid branch offset (arbitrary large offsets are not yet supported)", - Location: ctx.Declaration, - }, - { - Type: core.DebugResult, - Message: err.Error(), - }, - }) + label, results := aarch64translation.ArgumentToLabelInfo(info.Arguments[0]) + if !results.IsEmpty() { + return nil, results } - instruction := instructions.B(offset) - return instruction, core.ResultList{} + return label, core.ResultList{} } -type BranchDefinition struct{} - -func (BranchDefinition) BuildInstruction( +func (b Branch) PossibleNextSteps( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - results := core.ResultList{} +) (gen.StepInfo, core.ResultList) { + target, results := b.Target(info) + return gen.StepInfo{ + PossibleBranches: []*gen.LabelInfo{target}, + }, results +} +func (b Branch) Codegen( + ctx *aarch64codegen.InstructionCodegenContext, +) (instructions.Instruction, core.ResultList) { + info := ctx.InstructionInfo + + results := core.ResultList{} curResults := aarch64translation.AssertArgumentsExactly(info, 1) results.Extend(&curResults) @@ -66,16 +60,30 @@ func (BranchDefinition) BuildInstruction( return nil, results } - target, curResults := aarch64translation.ArgumentToLabelInfo(info.Arguments[0]) - results.Extend(&curResults) - + target, results := b.Target(info) if !results.IsEmpty() { return nil, results } - return Branch{Target: target}, core.ResultList{} -} + targetBasicBlock := target.BasicBlock + targetOffset := ctx.BasicBlockOffsets[targetBasicBlock] + currentOffset := ctx.InstructionOffsetInFunction + offset, err := aarch64translation.Uint64DiffToOffset26Align4(targetOffset, currentOffset) + + if err != nil { + return nil, list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: "Branch offset too large", + Location: ctx.Declaration, + }, + { + Type: core.DebugResult, + Message: err.Error(), + }, + }) + } -func NewBranchInstructionDefinition() gen.InstructionDefinition { - return BranchDefinition{} + inst := instructions.NewBranch(offset) + return inst, core.ResultList{} } From b6b9424cbfb1c2babe219b4bdc33e7b48ad35dda Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 10:38:55 +0300 Subject: [PATCH 05/33] Fixed `b.cond` --- aarch64/isa/bcond.go | 60 ++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/aarch64/isa/bcond.go b/aarch64/isa/bcond.go index b7ea46a..c37478f 100644 --- a/aarch64/isa/bcond.go +++ b/aarch64/isa/bcond.go @@ -12,16 +12,36 @@ import ( type Bcond struct { Condition immediates.Condition - Target *gen.LabelInfo } -func (b Bcond) Operator() string { +func (b Bcond) Target( + info *gen.InstructionInfo, +) (*gen.LabelInfo, core.ResultList) { + results := aarch64translation.AssertArgumentsExactly(info, 1) + if !results.IsEmpty() { + return nil, results + } + + target, results := aarch64translation.ArgumentToLabelInfo(info.Arguments[0]) + if !results.IsEmpty() { + return nil, results + } + + return target, core.ResultList{} +} + +func (b Bcond) Operator(*gen.InstructionInfo) string { return "b." + b.Condition.String() } -func (b Bcond) PossibleNextSteps() (gen.StepInfo, core.ResultList) { +func (b Bcond) PossibleNextSteps(info *gen.InstructionInfo) (gen.StepInfo, core.ResultList) { + target, results := b.Target(info) + if !results.IsEmpty() { + return gen.StepInfo{}, results + } + return gen.StepInfo{ - PossibleBranches: []*gen.LabelInfo{b.Target}, + PossibleBranches: []*gen.LabelInfo{target}, PossibleContinue: true, }, core.ResultList{} } @@ -29,7 +49,12 @@ func (b Bcond) PossibleNextSteps() (gen.StepInfo, core.ResultList) { func (b Bcond) Generate( ctx *aarch64codegen.InstructionCodegenContext, ) (instructions.Instruction, core.ResultList) { - targetBasicBlock := b.Target.BasicBlock + target, results := b.Target(ctx.InstructionInfo) + if !results.IsEmpty() { + return nil, results + } + + targetBasicBlock := target.BasicBlock targetOffset := ctx.BasicBlockOffsets[targetBasicBlock] currentOffset := ctx.InstructionOffsetInFunction offset, err := aarch64translation.Uint64DiffToOffset19Align4(targetOffset, currentOffset) @@ -52,35 +77,20 @@ func (b Bcond) Generate( return instruction, core.ResultList{} } -type BcondDefinition struct { - Condition immediates.Condition -} - -func (d BcondDefinition) BuildInstruction( +func (b Bcond) Validate( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +) core.ResultList { results := core.ResultList{} - curResults := aarch64translation.AssertArgumentsExactly(info, 1) + _, curResults := b.Target(info) results.Extend(&curResults) curResults = aarch64translation.AssertTargetsExactly(info, 0) results.Extend(&curResults) if !results.IsEmpty() { - return nil, results + return results } - target, curResults := aarch64translation.ArgumentToLabelInfo(info.Arguments[0]) - results.Extend(&curResults) - - if !results.IsEmpty() { - return nil, results - } - - return Bcond{Condition: d.Condition, Target: target}, core.ResultList{} -} - -func NewBcondInstructionDefinition(condition immediates.Condition) gen.InstructionDefinition { - return BcondDefinition{Condition: condition} + return core.ResultList{} } From 8b4d0e66dcecfc94fbb5030cf734ddcdab4bc187 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 10:46:26 +0300 Subject: [PATCH 06/33] Fixed `bl` --- aarch64/isa/bl.go | 96 ++++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/aarch64/isa/bl.go b/aarch64/isa/bl.go index 7ea566a..db9bb2d 100644 --- a/aarch64/isa/bl.go +++ b/aarch64/isa/bl.go @@ -11,23 +11,56 @@ import ( ) type Bl struct { - Target *gen.FunctionInfo + gen.NonBranchingInstruction } func (b Bl) Operator() string { return "bl" } -func (b Bl) PossibleNextSteps() (gen.StepInfo, core.ResultList) { - // TODO: add an analysis to check if the target function is a no-return - // function. - return gen.StepInfo{PossibleContinue: true}, core.ResultList{} +func (b Bl) Target( + info *gen.InstructionInfo, +) (*gen.FunctionInfo, core.ResultList) { + results := aarch64translation.AssertArgumentsExactly(info, 1) + if !results.IsEmpty() { + return nil, results + } + + target, results := aarch64translation.ArgumentToFunctionInfo(info.Arguments[0]) + if !results.IsEmpty() { + return nil, results + } + + return target, core.ResultList{} } -func (b Bl) registerRelocation(ctx *aarch64codegen.InstructionCodegenContext) { +func (b Bl) Validate(info *gen.InstructionInfo) core.ResultList { + results := core.ResultList{} + + _, curResults := b.Target(info) + results.Extend(&curResults) + + curResults = aarch64translation.AssertTargetsExactly(info, 0) + results.Extend(&curResults) + + if !results.IsEmpty() { + return results + } + + return core.ResultList{} +} + +func (b Bl) registerRelocation( + ctx *aarch64codegen.InstructionCodegenContext, +) core.ResultList { + target, results := b.Target(ctx.InstructionInfo) + if !results.IsEmpty() { + return results + } + relocation := section64.RelocationBuilder{ Address: uint32(ctx.InstructionOffsetInFile()), - SymbolIndex: ctx.FunctionIndices[b.Target], + SymbolIndex: ctx.FunctionIndices[target], IsRelocationPcRelative: true, Length: section64.RelocationLengthLong, IsRelocationExtern: true, @@ -35,17 +68,27 @@ func (b Bl) registerRelocation(ctx *aarch64codegen.InstructionCodegenContext) { } ctx.Relocations = append(ctx.Relocations, relocation) + return core.ResultList{} } func (b Bl) Generate( ctx *aarch64codegen.InstructionCodegenContext, ) (instructions.Instruction, core.ResultList) { - targetOffset, ok := ctx.FunctionOffsets[b.Target] + target, results := b.Target(ctx.InstructionInfo) + if !results.IsEmpty() { + return nil, results + } + + targetOffset, ok := ctx.FunctionOffsets[target] if !ok { // Target function is not defined: we add a relocation to the symbol // and let the linker resolve it. - b.registerRelocation(ctx) - return instructions.BL(0), core.ResultList{} + results = b.registerRelocation(ctx) + if !results.IsEmpty() { + return nil, results + } + + return instructions.NewBl(0), core.ResultList{} } currentOffset := ctx.InstructionOffsetInFile() @@ -65,36 +108,5 @@ func (b Bl) Generate( }) } - return instructions.BL(offset), core.ResultList{} -} - -type BlDefinition struct{} - -func (BlDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - results := core.ResultList{} - - curResults := aarch64translation.AssertArgumentsExactly(info, 1) - results.Extend(&curResults) - - curResults = aarch64translation.AssertTargetsExactly(info, 0) - results.Extend(&curResults) - - if !results.IsEmpty() { - return nil, results - } - - target, curResults := aarch64translation.ArgumentToFunctionInfo(info.Arguments[0]) - results.Extend(&curResults) - - if !results.IsEmpty() { - return nil, results - } - - return Bl{Target: target}, core.ResultList{} -} - -func NewBlInstructionDefinition() gen.InstructionDefinition { - return BlDefinition{} + return instructions.NewBl(offset), core.ResultList{} } From 627e458734e77d3422b42c603ebe23f47ca6d41c Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 10:57:04 +0300 Subject: [PATCH 07/33] Fixed `movz` --- aarch64/isa/movz.go | 84 ++++++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/aarch64/isa/movz.go b/aarch64/isa/movz.go index 33f8332..ef4dd74 100644 --- a/aarch64/isa/movz.go +++ b/aarch64/isa/movz.go @@ -1,7 +1,9 @@ package aarch64isa import ( + "alon.kr/x/aarch64codegen/immediates" "alon.kr/x/aarch64codegen/instructions" + "alon.kr/x/aarch64codegen/registers" aarch64codegen "alon.kr/x/usm/aarch64/codegen" aarch64translation "alon.kr/x/usm/aarch64/translation" "alon.kr/x/usm/core" @@ -9,39 +11,36 @@ import ( ) type Movz struct { - NonBranchingInstruction - instructions.Movz + gen.NonBranchingInstruction } -func (Movz) Operator() string { +func (Movz) Operator(*gen.InstructionInfo) string { return "movz" } -func (i Movz) Generate( - *aarch64codegen.InstructionCodegenContext, -) (instructions.Instruction, core.ResultList) { - return i, core.ResultList{} -} - -type MovzDefinition struct{} +func (i Movz) Xd(info *gen.InstructionInfo) (registers.GPRegister, core.ResultList) { + results := aarch64translation.AssertTargetsExactly(info, 1) -func (MovzDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - results := core.ResultList{} + if !results.IsEmpty() { + return registers.GPRegister(0), results + } - curResults := aarch64translation.AssertTargetsExactly(info, 1) - results.Extend(&curResults) + Xd, results := aarch64translation.TargetToAarch64GPRegister(info.Targets[0]) + if !results.IsEmpty() { + return registers.GPRegister(0), results + } - curResults = aarch64translation.AssertArgumentsBetween(info, 1, 2) - results.Extend(&curResults) + return Xd, core.ResultList{} +} +func (i Movz) Immediate( + info *gen.InstructionInfo, +) (immediates.Immediate16, instructions.MovShift, core.ResultList) { + results := aarch64translation.AssertArgumentsBetween(info, 1, 2) if !results.IsEmpty() { - return nil, results - } + return immediates.Immediate16(0), instructions.MovShift0, results - Xd, curResults := aarch64translation.TargetToAarch64GPRegister(info.Targets[0]) - results.Extend(&curResults) + } imm, curResults := aarch64translation.ArgumentToAarch64Immediate16(info.Arguments[0]) results.Extend(&curResults) @@ -55,14 +54,43 @@ func (MovzDefinition) BuildInstruction( } if !results.IsEmpty() { - return nil, results + return immediates.Immediate16(0), instructions.MovShift0, results } - return Movz{ - Movz: instructions.MOVZ(Xd, imm, shift), - }, core.ResultList{} + return imm, shift, core.ResultList{} } -func NewMovzInstructionDefinition() gen.InstructionDefinition { - return MovzDefinition{} +func (i Movz) Validate(info *gen.InstructionInfo) core.ResultList { + results := core.ResultList{} + + _, curResults := i.Xd(info) + results.Extend(&curResults) + + _, _, curResults = i.Immediate(info) + results.Extend(&curResults) + + if !results.IsEmpty() { + return results + } + + return core.ResultList{} +} + +func (i Movz) Codegen( + _ *aarch64codegen.InstructionCodegenContext, + info *gen.InstructionInfo, +) (instructions.Instruction, core.ResultList) { + results := core.ResultList{} + + Xd, curResults := i.Xd(info) + results.Extend(&curResults) + + imm, shift, curResults := i.Immediate(info) + results.Extend(&curResults) + + if !results.IsEmpty() { + return nil, results + } + + return instructions.MOVZ(Xd, imm, shift), core.ResultList{} } From 9be7356c862a404e2d8f42c6e3972598100da9e7 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 11:13:58 +0300 Subject: [PATCH 08/33] Fixed `ret` --- aarch64/isa/ret.go | 55 +++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/aarch64/isa/ret.go b/aarch64/isa/ret.go index e75a070..26153b9 100644 --- a/aarch64/isa/ret.go +++ b/aarch64/isa/ret.go @@ -9,56 +9,51 @@ import ( "alon.kr/x/usm/gen" ) -type Ret struct { - instructions.Ret -} +type Ret struct{} -func (Ret) Operator() string { +func (Ret) Operator(*gen.InstructionInfo) string { return "ret" } -func (Ret) PossibleNextSteps() (gen.StepInfo, core.ResultList) { +func (Ret) PossibleNextSteps(*gen.InstructionInfo) (gen.StepInfo, core.ResultList) { return gen.StepInfo{PossibleReturn: true}, core.ResultList{} } -func (i Ret) Generate( +func (i Ret) Codegen( *aarch64codegen.InstructionCodegenContext, ) (instructions.Instruction, core.ResultList) { - return i, core.ResultList{} + return instructions.RET(registers.GPRegisterX30), core.ResultList{} } -type RetDefinition struct{} +func (i Ret) Xn(info *gen.InstructionInfo) (registers.GPRegister, core.ResultList) { + results := aarch64translation.AssertArgumentsBetween(info, 0, 1) + if !results.IsEmpty() { + return registers.GPRegister(0), results + } + + Xn := registers.GPRegisterX30 + if len(info.Arguments) > 0 { + Xn, results = aarch64translation.ArgumentToAarch64GPRegister(info.Arguments[0]) + if !results.IsEmpty() { + return registers.GPRegister(0), results + } + } + + return Xn, core.ResultList{} +} -func (RetDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +func (i Ret) Validate(info *gen.InstructionInfo) core.ResultList { results := core.ResultList{} - curResults := aarch64translation.AssertArgumentsBetween(info, 0, 1) + _, curResults := i.Xn(info) results.Extend(&curResults) curResults = aarch64translation.AssertTargetsExactly(info, 0) results.Extend(&curResults) if !results.IsEmpty() { - return nil, results + return results } - Xn := registers.GPRegisterX30 - if len(info.Arguments) > 0 { - Xn, curResults = aarch64translation.ArgumentToAarch64GPRegister(info.Arguments[0]) - results.Extend(&curResults) - } - - if !results.IsEmpty() { - return nil, results - } - - return Ret{ - instructions.RET(Xn), - }, core.ResultList{} -} - -func NewRetInstructionDefinition() gen.InstructionDefinition { - return RetDefinition{} + return core.ResultList{} } From 98045868be4f6838688509a387166a7e99bdc324 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 11:20:22 +0300 Subject: [PATCH 09/33] Fixed `sub` --- aarch64/isa/sub.go | 83 +++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 53 deletions(-) diff --git a/aarch64/isa/sub.go b/aarch64/isa/sub.go index 67c411d..b90bf5a 100644 --- a/aarch64/isa/sub.go +++ b/aarch64/isa/sub.go @@ -9,86 +9,56 @@ import ( "alon.kr/x/usm/gen" ) -type BaseSub struct { - NonBranchingInstruction +type Sub struct { + gen.NonBranchingInstruction } -func (BaseSub) Operator() string { - return "sub" -} - -type SubReg struct { - BaseSub - instructions.SubShiftedRegister -} - -func (i SubReg) Generate( - *aarch64codegen.InstructionCodegenContext, -) (instructions.Instruction, core.ResultList) { - return i, core.ResultList{} +func NewSubInstruction() gen.InstructionDefinition { + return Sub{} } -type SubImm struct { - BaseSub - instructions.SubImmediate -} - -func (i SubImm) Generate( - *aarch64codegen.InstructionCodegenContext, -) (instructions.Instruction, core.ResultList) { - return i, core.ResultList{} +func (Sub) Operator(*gen.InstructionInfo) string { + return "sub" } -type SubDefinition struct{} - -func (SubDefinition) buildRegisterVariant( +func (Sub) codegenRegisterVariant( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +) (instructions.Instruction, core.ResultList) { Xd, Xn, Xm, results := aarch64translation.BinaryInstructionToAarch64(info) if !results.IsEmpty() { return nil, results } - return SubReg{ - SubShiftedRegister: instructions.NewSubShiftedRegister(Xd, Xn, Xm), - }, core.ResultList{} + inst := instructions.NewSubShiftedRegister(Xd, Xn, Xm) + return inst, core.ResultList{} } -func (SubDefinition) buildImmediateVariant( +func (Sub) codegenImmediateVariant( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +) (instructions.Instruction, core.ResultList) { Xd, Xn, imm, results := aarch64translation.Immediate12GPRegisterTargetInstructionToAarch64(info) if !results.IsEmpty() { return nil, results } - return SubImm{ - SubImmediate: instructions.NewSubImmediate(Xd, Xn, imm), - }, core.ResultList{} + inst := instructions.NewSubImmediate(Xd, Xn, imm) + return inst, core.ResultList{} } -func (d SubDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - results := core.ResultList{} - - curResults := aarch64translation.AssertTargetsExactly(info, 1) - results.Extend(&curResults) - - curResults = aarch64translation.AssertArgumentsExactly(info, 2) - results.Extend(&curResults) - +func (i Sub) Codegen( + ctx *aarch64codegen.InstructionCodegenContext, +) (instructions.Instruction, core.ResultList) { + info := ctx.InstructionInfo + results := aarch64translation.ValidateBinaryInstruction(info) if !results.IsEmpty() { return nil, results } switch info.Arguments[1].(type) { case *gen.RegisterArgumentInfo: - return d.buildRegisterVariant(info) - + return i.codegenRegisterVariant(info) case *gen.ImmediateInfo: - return d.buildImmediateVariant(info) - + return i.codegenImmediateVariant(info) default: return nil, list.FromSingle(core.Result{ { @@ -100,6 +70,13 @@ func (d SubDefinition) BuildInstruction( } } -func NewSubInstructionDefinition() gen.InstructionDefinition { - return SubDefinition{} +func (i Sub) Validate( + info *gen.InstructionInfo, +) core.ResultList { + // TODO: this is a pretty hacky way to validate the instruction: we create + // a "mock" generation context, and then try to generate the binary + // representation of the instruction. + ctx := aarch64codegen.InstructionCodegenContext{InstructionInfo: info} + _, results := i.Codegen(&ctx) + return results } From 9deea863fd6b6d5875cdd4c50d207889f9f6d3b8 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 11:29:45 +0300 Subject: [PATCH 10/33] Added internal validation to `b` --- aarch64/isa/b.go | 45 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/aarch64/isa/b.go b/aarch64/isa/b.go index 68172fb..81e5c3b 100644 --- a/aarch64/isa/b.go +++ b/aarch64/isa/b.go @@ -11,15 +11,15 @@ import ( type Branch struct{} -func NewBranch() Branch { +func NewBranch() gen.InstructionDefinition { return Branch{} } -func (b Branch) Operator() string { +func (i Branch) Operator(*gen.InstructionInfo) string { return "b" } -func (b Branch) Target( +func (i Branch) Target( info *gen.InstructionInfo, ) (*gen.LabelInfo, core.ResultList) { results := aarch64translation.AssertArgumentsExactly(info, 1) @@ -35,22 +35,25 @@ func (b Branch) Target( return label, core.ResultList{} } -func (b Branch) PossibleNextSteps( +func (i Branch) PossibleNextSteps( info *gen.InstructionInfo, ) (gen.StepInfo, core.ResultList) { - target, results := b.Target(info) + target, results := i.Target(info) return gen.StepInfo{ PossibleBranches: []*gen.LabelInfo{target}, }, results } -func (b Branch) Codegen( - ctx *aarch64codegen.InstructionCodegenContext, -) (instructions.Instruction, core.ResultList) { - info := ctx.InstructionInfo +type branchValidationArtifacts struct { + Target *gen.LabelInfo +} +func (i Branch) internalValidate( + info *gen.InstructionInfo, +) (*branchValidationArtifacts, core.ResultList) { results := core.ResultList{} - curResults := aarch64translation.AssertArgumentsExactly(info, 1) + + target, curResults := i.Target(info) results.Extend(&curResults) curResults = aarch64translation.AssertTargetsExactly(info, 0) @@ -60,11 +63,24 @@ func (b Branch) Codegen( return nil, results } - target, results := b.Target(info) + artifacts := &branchValidationArtifacts{ + Target: target, + } + + return artifacts, core.ResultList{} +} + +func (i Branch) Codegen( + ctx *aarch64codegen.InstructionCodegenContext, +) (instructions.Instruction, core.ResultList) { + info := ctx.InstructionInfo + + artifacts, results := i.internalValidate(info) if !results.IsEmpty() { return nil, results } + target := artifacts.Target targetBasicBlock := target.BasicBlock targetOffset := ctx.BasicBlockOffsets[targetBasicBlock] currentOffset := ctx.InstructionOffsetInFunction @@ -87,3 +103,10 @@ func (b Branch) Codegen( inst := instructions.NewBranch(offset) return inst, core.ResultList{} } + +func (i Branch) Validate( + info *gen.InstructionInfo, +) core.ResultList { + _, results := i.internalValidate(info) + return results +} From 4fb0057d88f3aacec54e25d53f7342400c7ddd92 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 11:32:02 +0300 Subject: [PATCH 11/33] Added `New` functions for instruction definitions --- aarch64/isa/add.go | 2 +- aarch64/isa/add_test.go | 2 +- aarch64/isa/adds.go | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/aarch64/isa/add.go b/aarch64/isa/add.go index 140836c..d8d3335 100644 --- a/aarch64/isa/add.go +++ b/aarch64/isa/add.go @@ -13,7 +13,7 @@ type Add struct { gen.NonBranchingInstruction } -func NewAddInstruction() gen.InstructionDefinition { +func NewAdd() gen.InstructionDefinition { return Add{} } diff --git a/aarch64/isa/add_test.go b/aarch64/isa/add_test.go index 704eed3..c68a292 100644 --- a/aarch64/isa/add_test.go +++ b/aarch64/isa/add_test.go @@ -65,7 +65,7 @@ func assertExpectedCodegen( } func TestAddExpectedCodegen(t *testing.T) { - def := aarch64isa.NewAddInstruction() + def := aarch64isa.NewAdd() testCases := []struct { src string diff --git a/aarch64/isa/adds.go b/aarch64/isa/adds.go index cfa53df..e421339 100644 --- a/aarch64/isa/adds.go +++ b/aarch64/isa/adds.go @@ -13,7 +13,11 @@ type Adds struct { gen.NonBranchingInstruction } -func (Adds) Operator() string { +func NewAdds() gen.InstructionDefinition { + return Adds{} +} + +func (Adds) Operator(*gen.InstructionInfo) string { return "adds" } From cc5ae726b967acfb82bd5eac1f3cffaec6d06c41 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 11:41:33 +0300 Subject: [PATCH 12/33] Fixed all aarch64 isa instructions --- aarch64/isa/bcond.go | 8 +++- aarch64/isa/bl.go | 8 +++- aarch64/isa/movz.go | 8 +++- aarch64/isa/ret.go | 14 +++++- aarch64/isa/sub.go | 2 +- aarch64/isa/subs.go | 73 +++++++++++++------------------- aarch64/managers/instructions.go | 48 ++++++++++----------- 7 files changed, 86 insertions(+), 75 deletions(-) diff --git a/aarch64/isa/bcond.go b/aarch64/isa/bcond.go index c37478f..11072e5 100644 --- a/aarch64/isa/bcond.go +++ b/aarch64/isa/bcond.go @@ -14,6 +14,12 @@ type Bcond struct { Condition immediates.Condition } +func NewBcond(condition immediates.Condition) gen.InstructionDefinition { + return Bcond{ + Condition: condition, + } +} + func (b Bcond) Target( info *gen.InstructionInfo, ) (*gen.LabelInfo, core.ResultList) { @@ -46,7 +52,7 @@ func (b Bcond) PossibleNextSteps(info *gen.InstructionInfo) (gen.StepInfo, core. }, core.ResultList{} } -func (b Bcond) Generate( +func (b Bcond) Codegen( ctx *aarch64codegen.InstructionCodegenContext, ) (instructions.Instruction, core.ResultList) { target, results := b.Target(ctx.InstructionInfo) diff --git a/aarch64/isa/bl.go b/aarch64/isa/bl.go index db9bb2d..50b3e1e 100644 --- a/aarch64/isa/bl.go +++ b/aarch64/isa/bl.go @@ -14,7 +14,11 @@ type Bl struct { gen.NonBranchingInstruction } -func (b Bl) Operator() string { +func NewBl() gen.InstructionDefinition { + return Bl{} +} + +func (b Bl) Operator(*gen.InstructionInfo) string { return "bl" } @@ -71,7 +75,7 @@ func (b Bl) registerRelocation( return core.ResultList{} } -func (b Bl) Generate( +func (b Bl) Codegen( ctx *aarch64codegen.InstructionCodegenContext, ) (instructions.Instruction, core.ResultList) { target, results := b.Target(ctx.InstructionInfo) diff --git a/aarch64/isa/movz.go b/aarch64/isa/movz.go index ef4dd74..96b281e 100644 --- a/aarch64/isa/movz.go +++ b/aarch64/isa/movz.go @@ -14,6 +14,10 @@ type Movz struct { gen.NonBranchingInstruction } +func NewMovz() gen.InstructionDefinition { + return Movz{} +} + func (Movz) Operator(*gen.InstructionInfo) string { return "movz" } @@ -77,9 +81,9 @@ func (i Movz) Validate(info *gen.InstructionInfo) core.ResultList { } func (i Movz) Codegen( - _ *aarch64codegen.InstructionCodegenContext, - info *gen.InstructionInfo, + ctx *aarch64codegen.InstructionCodegenContext, ) (instructions.Instruction, core.ResultList) { + info := ctx.InstructionInfo results := core.ResultList{} Xd, curResults := i.Xd(info) diff --git a/aarch64/isa/ret.go b/aarch64/isa/ret.go index 26153b9..ad5628b 100644 --- a/aarch64/isa/ret.go +++ b/aarch64/isa/ret.go @@ -11,6 +11,10 @@ import ( type Ret struct{} +func NewRet() gen.InstructionDefinition { + return Ret{} +} + func (Ret) Operator(*gen.InstructionInfo) string { return "ret" } @@ -20,9 +24,15 @@ func (Ret) PossibleNextSteps(*gen.InstructionInfo) (gen.StepInfo, core.ResultLis } func (i Ret) Codegen( - *aarch64codegen.InstructionCodegenContext, + ctx *aarch64codegen.InstructionCodegenContext, ) (instructions.Instruction, core.ResultList) { - return instructions.RET(registers.GPRegisterX30), core.ResultList{} + info := ctx.InstructionInfo + Xn, results := i.Xn(info) + if !results.IsEmpty() { + return nil, results + } + + return instructions.RET(Xn), core.ResultList{} } func (i Ret) Xn(info *gen.InstructionInfo) (registers.GPRegister, core.ResultList) { diff --git a/aarch64/isa/sub.go b/aarch64/isa/sub.go index b90bf5a..e29d9ab 100644 --- a/aarch64/isa/sub.go +++ b/aarch64/isa/sub.go @@ -13,7 +13,7 @@ type Sub struct { gen.NonBranchingInstruction } -func NewSubInstruction() gen.InstructionDefinition { +func NewSub() gen.InstructionDefinition { return Sub{} } diff --git a/aarch64/isa/subs.go b/aarch64/isa/subs.go index 35f98ff..458c6df 100644 --- a/aarch64/isa/subs.go +++ b/aarch64/isa/subs.go @@ -9,67 +9,47 @@ import ( "alon.kr/x/usm/gen" ) -type BaseSubs struct { - NonBranchingInstruction +type Subs struct { + gen.NonBranchingInstruction } -func (BaseSubs) Operator() string { - return "subs" -} - -type SubsReg struct { - BaseSubs - instructions.SubShiftedRegister +func NewSubs() gen.InstructionDefinition { + return Subs{} } -func (i SubsReg) Generate( - *aarch64codegen.InstructionCodegenContext, -) (instructions.Instruction, core.ResultList) { - return i, core.ResultList{} -} - -type SubsImm struct { - BaseSubs - instructions.SubImmediate -} - -func (i SubsImm) Generate( - *aarch64codegen.InstructionCodegenContext, -) (instructions.Instruction, core.ResultList) { - return i, core.ResultList{} +func (Subs) Operator(*gen.InstructionInfo) string { + return "subs" } -type SubsDefinition struct{} - -func (d SubsDefinition) buildRegisterVariant( +func (i Subs) codegenRegisterVariant( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +) (instructions.Instruction, core.ResultList) { Xd, Xn, Xm, results := aarch64translation.BinaryInstructionToAarch64(info) if !results.IsEmpty() { return nil, results } - return SubsReg{ - SubShiftedRegister: instructions.NewSubsShiftedRegister(Xd, Xn, Xm), - }, core.ResultList{} + inst := instructions.NewSubsShiftedRegister(Xd, Xn, Xm) + return inst, core.ResultList{} } -func (SubsDefinition) buildImmediateVariant( +func (i Subs) codegenImmediateVariant( info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +) (instructions.Instruction, core.ResultList) { Xd, Xn, imm, results := aarch64translation.Immediate12GPRegisterTargetInstructionToAarch64(info) if !results.IsEmpty() { return nil, results } - return SubsImm{ - SubImmediate: instructions.NewSubsImmediate(Xd, Xn, imm), - }, core.ResultList{} + inst := instructions.NewSubsImmediate(Xd, Xn, imm) + return inst, core.ResultList{} } -func (d SubsDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { +func (i Subs) Codegen( + ctx *aarch64codegen.InstructionCodegenContext, +) (instructions.Instruction, core.ResultList) { + info := ctx.InstructionInfo + results := aarch64translation.ValidateBinaryInstruction(info) if !results.IsEmpty() { return nil, results @@ -77,10 +57,10 @@ func (d SubsDefinition) BuildInstruction( switch info.Arguments[1].(type) { case *gen.RegisterArgumentInfo: - return d.buildRegisterVariant(info) + return i.codegenRegisterVariant(info) case *gen.ImmediateInfo: - return d.buildImmediateVariant(info) + return i.codegenImmediateVariant(info) default: return nil, list.FromSingle(core.Result{ @@ -93,6 +73,13 @@ func (d SubsDefinition) BuildInstruction( } } -func NewSubsInstructionDefinition() gen.InstructionDefinition { - return SubsDefinition{} +func (i Subs) Validate( + info *gen.InstructionInfo, +) core.ResultList { + // TODO: this is a pretty hacky way to validate the instruction: we create + // a "mock" generation context, and then try to generate the binary + // representation of the instruction. + ctx := aarch64codegen.InstructionCodegenContext{InstructionInfo: info} + _, results := i.Codegen(&ctx) + return results } diff --git a/aarch64/managers/instructions.go b/aarch64/managers/instructions.go index eef6329..27f4cb7 100644 --- a/aarch64/managers/instructions.go +++ b/aarch64/managers/instructions.go @@ -11,36 +11,36 @@ func NewInstructionManager() gen.InstructionManager { return gen.NewInstructionMap( []faststringmap.MapEntry[gen.InstructionDefinition]{ // Move - {Key: "movz", Value: aarch64isa.NewMovzInstructionDefinition()}, + {Key: "movz", Value: aarch64isa.NewMovz()}, // Arithmetic - {Key: "add", Value: aarch64isa.NewAddInstructionDefinition()}, - {Key: "adds", Value: aarch64isa.NewAddsInstructionDefinition()}, - {Key: "sub", Value: aarch64isa.NewSubInstructionDefinition()}, - {Key: "subs", Value: aarch64isa.NewSubsInstructionDefinition()}, + {Key: "add", Value: aarch64isa.NewAdd()}, + {Key: "adds", Value: aarch64isa.NewAdds()}, + {Key: "sub", Value: aarch64isa.NewSub()}, + {Key: "subs", Value: aarch64isa.NewSubs()}, // Control flow - {Key: "b", Value: aarch64isa.NewBranchInstructionDefinition()}, - {Key: "bl", Value: aarch64isa.NewBlInstructionDefinition()}, - {Key: "ret", Value: aarch64isa.NewRetInstructionDefinition()}, + {Key: "b", Value: aarch64isa.NewBranch()}, + {Key: "bl", Value: aarch64isa.NewBl()}, + {Key: "ret", Value: aarch64isa.NewRet()}, // Conditional branches - {Key: "b.eq", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionEq)}, - {Key: "b.ne", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionNe)}, - {Key: "b.cs", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionCs)}, - {Key: "b.cc", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionCc)}, - {Key: "b.mi", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionMi)}, - {Key: "b.pl", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionPl)}, - {Key: "b.vs", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionVs)}, - {Key: "b.vc", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionVc)}, - {Key: "b.hi", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionHi)}, - {Key: "b.ls", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionLs)}, - {Key: "b.ge", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionGe)}, - {Key: "b.lt", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionLt)}, - {Key: "b.gt", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionGt)}, - {Key: "b.le", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionLe)}, - {Key: "b.al", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionAl)}, - {Key: "b.nv", Value: aarch64isa.NewBcondInstructionDefinition(immediates.ConditionNv)}, + {Key: "b.eq", Value: aarch64isa.NewBcond(immediates.ConditionEq)}, + {Key: "b.ne", Value: aarch64isa.NewBcond(immediates.ConditionNe)}, + {Key: "b.cs", Value: aarch64isa.NewBcond(immediates.ConditionCs)}, + {Key: "b.cc", Value: aarch64isa.NewBcond(immediates.ConditionCc)}, + {Key: "b.mi", Value: aarch64isa.NewBcond(immediates.ConditionMi)}, + {Key: "b.pl", Value: aarch64isa.NewBcond(immediates.ConditionPl)}, + {Key: "b.vs", Value: aarch64isa.NewBcond(immediates.ConditionVs)}, + {Key: "b.vc", Value: aarch64isa.NewBcond(immediates.ConditionVc)}, + {Key: "b.hi", Value: aarch64isa.NewBcond(immediates.ConditionHi)}, + {Key: "b.ls", Value: aarch64isa.NewBcond(immediates.ConditionLs)}, + {Key: "b.ge", Value: aarch64isa.NewBcond(immediates.ConditionGe)}, + {Key: "b.lt", Value: aarch64isa.NewBcond(immediates.ConditionLt)}, + {Key: "b.gt", Value: aarch64isa.NewBcond(immediates.ConditionGt)}, + {Key: "b.le", Value: aarch64isa.NewBcond(immediates.ConditionLe)}, + {Key: "b.al", Value: aarch64isa.NewBcond(immediates.ConditionAl)}, + {Key: "b.nv", Value: aarch64isa.NewBcond(immediates.ConditionNv)}, }, false, ) From 2bb13168c0ddda7bc3f957418b1245e8ea05fb56 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 11:50:32 +0300 Subject: [PATCH 13/33] Fixed `gen` package tests --- gen/function_generator_test.go | 28 +++--- gen/instruction_generator_test.go | 150 +++++++----------------------- gen/utils_test.go | 8 +- 3 files changed, 54 insertions(+), 132 deletions(-) diff --git a/gen/function_generator_test.go b/gen/function_generator_test.go index 26084f0..506ed25 100644 --- a/gen/function_generator_test.go +++ b/gen/function_generator_test.go @@ -37,9 +37,9 @@ func generateFunctionFromSource( func TestSimpleFunctionGeneration(t *testing.T) { src := `func $32 @add $32 %a { .entry - $32 %b = ADD %a $32 #1 - $32 %c = ADD %b %a - RET + $32 %b = add %a $32 #1 + $32 %c = add %b %a + ret } ` @@ -90,14 +90,14 @@ func TestSimpleFunctionGeneration(t *testing.T) { func TestIfElseFunctionGeneration(t *testing.T) { src := `func @toBool $32 %n { .entry - JZ %n .zero + jz %n .zero .nonzero - $32 %bool = ADD $32 #1 $32 #0 - JMP .end + $32 %bool = add $32 #1 $32 #0 + j .end .zero - $32 %bool = ADD $32 #0 $32 #0 + $32 %bool = add $32 #0 $32 #0 .end - RET + ret } ` @@ -131,7 +131,7 @@ func TestEmptyFunctionGeneration(t *testing.T) { func TestNoReturnFunctionGeneration(t *testing.T) { src := `func @noReturn { - $32 %n = ADD $32 #1 $32 #2 + $32 %n = add $32 #1 $32 #2 }` function, results := generateFunctionFromSource(t, src) assert.False(t, results.IsEmpty()) @@ -142,8 +142,8 @@ func TestNoReturnFunctionGeneration(t *testing.T) { func TestNoExplicitRegisterType(t *testing.T) { src := `func @noExplicitType { - %a = ADD $32 #1 $32 #2 - RET + %a = add $32 #1 $32 #2 + ret }` function, results := generateFunctionFromSource(t, src) @@ -155,9 +155,9 @@ func TestNoExplicitRegisterType(t *testing.T) { func TestExplicitRegisterDefinitionNotOnSecondSight(t *testing.T) { src := `func @main { - %a = ADD $32 #0 $32 #0 - $32 %a = ADD %a $32 #1 - RET + %a = add $32 #0 $32 #0 + $32 %a = add %a $32 #1 + ret }` function, results := generateFunctionFromSource(t, src) assert.True(t, results.IsEmpty()) diff --git a/gen/instruction_generator_test.go b/gen/instruction_generator_test.go index 506688a..637014e 100644 --- a/gen/instruction_generator_test.go +++ b/gen/instruction_generator_test.go @@ -14,100 +14,50 @@ import ( // MARK: Add -type AddInstruction struct{} - -func (AddInstruction) PossibleNextSteps(*gen.InstructionInfo) (gen.StepInfo, core.ResultList) { - return gen.StepInfo{PossibleContinue: true}, core.ResultList{} -} - -func (AddInstruction) Operator(*gen.InstructionInfo) string { - return "ADD" +type Add struct { + gen.NonBranchingInstruction } -func (AddInstruction) Validate(info *gen.InstructionInfo) core.ResultList { - return core.ResultList{} +func NewAdd() gen.InstructionDefinition { + return Add{} } -type AddInstructionDefinition struct{} - -func (AddInstructionDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.InstructionDefinition, core.ResultList) { - return gen.InstructionDefinition(AddInstruction{}), core.ResultList{} +func (Add) Operator(*gen.InstructionInfo) string { + return "add" } -func (AddInstructionDefinition) InferTargetTypes( - ctx *gen.FunctionGenerationContext, - targets []*gen.ReferencedTypeInfo, - arguments []*gen.ReferencedTypeInfo, -) ([]gen.ReferencedTypeInfo, core.ResultList) { - if len(arguments) != 2 { - return nil, list.FromSingle(core.Result{{ - Type: core.ErrorResult, - Message: "expected exactly 2 arguments", - }}) - } - - if len(targets) != 1 { - return nil, list.FromSingle(core.Result{{ - Type: core.ErrorResult, - Message: "expected exactly 1 target", - }}) - } - - // TODO: possible panic? - if !arguments[0].Equal(*arguments[1]) { - return nil, list.FromSingle(core.Result{{ - Type: core.ErrorResult, - Message: "expected both arguments to be of the same type", - }}) - } - - return []gen.ReferencedTypeInfo{ - { - Base: arguments[0].Base, - Descriptors: arguments[0].Descriptors, - }, - }, core.ResultList{} +func (Add) Validate(info *gen.InstructionInfo) core.ResultList { + return core.ResultList{} } // MARK: Ret -type RetInstruction struct{} +type Ret struct{} -func (RetInstruction) PossibleNextSteps(*gen.InstructionInfo) (gen.StepInfo, core.ResultList) { - return gen.StepInfo{PossibleReturn: true}, core.ResultList{} +func NewRet() gen.InstructionDefinition { + return Ret{} } -func (RetInstruction) Operator(*gen.InstructionInfo) string { - return "RET" +func (Ret) Operator(*gen.InstructionInfo) string { + return "ret" +} +func (Ret) PossibleNextSteps(*gen.InstructionInfo) (gen.StepInfo, core.ResultList) { + return gen.StepInfo{PossibleReturn: true}, core.ResultList{} } -func (RetInstruction) Validate(*gen.InstructionInfo) core.ResultList { +func (Ret) Validate(*gen.InstructionInfo) core.ResultList { return core.ResultList{} } -type RetInstructionDefinition struct{} +// MARK: Jump -func (RetInstructionDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.InstructionDefinition, core.ResultList) { - return gen.InstructionDefinition(RetInstruction{}), core.ResultList{} -} +type Jump struct{} -func (RetInstructionDefinition) InferTargetTypes( - ctx *gen.FunctionGenerationContext, - targets []*gen.ReferencedTypeInfo, - arguments []*gen.ReferencedTypeInfo, -) ([]gen.ReferencedTypeInfo, core.ResultList) { - return []gen.ReferencedTypeInfo{}, core.ResultList{} +func NewJump() gen.InstructionDefinition { + return Jump{} } -// MARK: Jump - -type JumpInstruction struct{} - -func (JumpInstruction) PossibleNextSteps(i *gen.InstructionInfo) (gen.StepInfo, core.ResultList) { +func (Jump) PossibleNextSteps(i *gen.InstructionInfo) (gen.StepInfo, core.ResultList) { return gen.StepInfo{ PossibleBranches: []*gen.LabelInfo{ i.Arguments[0].(*gen.LabelArgumentInfo).Label, @@ -115,36 +65,28 @@ func (JumpInstruction) PossibleNextSteps(i *gen.InstructionInfo) (gen.StepInfo, }, core.ResultList{} } -func (JumpInstruction) Operator(*gen.InstructionInfo) string { - return "JMP" +func (Jump) Operator(*gen.InstructionInfo) string { + return "j" } -func (JumpInstruction) Validate(info *gen.InstructionInfo) core.ResultList { +func (Jump) Validate(info *gen.InstructionInfo) core.ResultList { return core.ResultList{} } -type JumpInstructionDefinition struct{} +// MARK: Jump Zero -func (JumpInstructionDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.InstructionDefinition, core.ResultList) { - return gen.InstructionDefinition(JumpInstruction{}), core.ResultList{} -} +// JZ %condition .label +type JumpZero struct{} -func (JumpInstructionDefinition) InferTargetTypes( - ctx *gen.FunctionGenerationContext, - targets []*gen.ReferencedTypeInfo, - arguments []*gen.ReferencedTypeInfo, -) ([]gen.ReferencedTypeInfo, core.ResultList) { - return []gen.ReferencedTypeInfo{}, core.ResultList{} +func NewJumpZero() gen.InstructionDefinition { + return JumpZero{} } -// MARK: Jump Zero - -// JZ %condition .label -type JumpZeroInstruction struct{} +func (JumpZero) Operator(*gen.InstructionInfo) string { + return "jz" +} -func (JumpZeroInstruction) PossibleNextSteps(i *gen.InstructionInfo) (gen.StepInfo, core.ResultList) { +func (JumpZero) PossibleNextSteps(i *gen.InstructionInfo) (gen.StepInfo, core.ResultList) { label := i.Arguments[1].(*gen.LabelArgumentInfo).Label return gen.StepInfo{ PossibleBranches: []*gen.LabelInfo{label}, @@ -152,30 +94,10 @@ func (JumpZeroInstruction) PossibleNextSteps(i *gen.InstructionInfo) (gen.StepIn }, core.ResultList{} } -func (JumpZeroInstruction) Operator(*gen.InstructionInfo) string { - return "JZ" -} - -func (JumpZeroInstruction) Validate(info *gen.InstructionInfo) core.ResultList { +func (JumpZero) Validate(info *gen.InstructionInfo) core.ResultList { return core.ResultList{} } -type JumpZeroInstructionDefinition struct{} - -func (JumpZeroInstructionDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.InstructionDefinition, core.ResultList) { - return gen.InstructionDefinition(JumpZeroInstruction{}), core.ResultList{} -} - -func (JumpZeroInstructionDefinition) InferTargetTypes( - ctx *gen.FunctionGenerationContext, - targets []*gen.ReferencedTypeInfo, - arguments []*gen.ReferencedTypeInfo, -) ([]gen.ReferencedTypeInfo, core.ResultList) { - return []gen.ReferencedTypeInfo{}, core.ResultList{} -} - // MARK: Instruction Map type InstructionMap map[string]gen.InstructionDefinition @@ -234,7 +156,7 @@ func PrepareTestForInstructionGeneration( } func TestInstructionCreateTarget(t *testing.T) { - src := core.NewSourceView("$32 %c = ADD %a %b\n") + src := core.NewSourceView("$32 %c = add %a %b\n") node, ctx := PrepareTestForInstructionGeneration(src, t) generator := gen.NewInstructionGenerator() diff --git a/gen/utils_test.go b/gen/utils_test.go index f67bd21..5488798 100644 --- a/gen/utils_test.go +++ b/gen/utils_test.go @@ -67,10 +67,10 @@ func (m *RegisterMap) GetAllRegisters() []*gen.RegisterInfo { var testInstructionSet = gen.InstructionManager( &InstructionMap{ - "ADD": &AddInstructionDefinition{}, - "JMP": &JumpInstructionDefinition{}, - "JZ": &JumpZeroInstructionDefinition{}, - "RET": &RetInstructionDefinition{}, + "add": NewAdd(), + "j": NewJump(), + "jz": NewJumpZero(), + "ret": NewRet(), }, ) From 538483aced855cd12b7b70c07ec923d2f3b04929 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 12:16:05 +0300 Subject: [PATCH 14/33] Moved general asserts to `gen` package --- aarch64/isa/b.go | 4 +- aarch64/isa/bcond.go | 4 +- aarch64/isa/bl.go | 4 +- aarch64/isa/movz.go | 4 +- aarch64/isa/ret.go | 4 +- aarch64/translation/arguments.go | 112 +--------------------- aarch64/translation/instructions.go | 4 +- aarch64/translation/targets.go | 20 ---- gen/assert.go | 141 ++++++++++++++++++++++++++++ 9 files changed, 154 insertions(+), 143 deletions(-) create mode 100644 gen/assert.go diff --git a/aarch64/isa/b.go b/aarch64/isa/b.go index 81e5c3b..015b5a8 100644 --- a/aarch64/isa/b.go +++ b/aarch64/isa/b.go @@ -22,7 +22,7 @@ func (i Branch) Operator(*gen.InstructionInfo) string { func (i Branch) Target( info *gen.InstructionInfo, ) (*gen.LabelInfo, core.ResultList) { - results := aarch64translation.AssertArgumentsExactly(info, 1) + results := gen.AssertArgumentsExactly(info, 1) if !results.IsEmpty() { return nil, results } @@ -56,7 +56,7 @@ func (i Branch) internalValidate( target, curResults := i.Target(info) results.Extend(&curResults) - curResults = aarch64translation.AssertTargetsExactly(info, 0) + curResults = gen.AssertTargetsExactly(info, 0) results.Extend(&curResults) if !results.IsEmpty() { diff --git a/aarch64/isa/bcond.go b/aarch64/isa/bcond.go index 11072e5..76f5cd4 100644 --- a/aarch64/isa/bcond.go +++ b/aarch64/isa/bcond.go @@ -23,7 +23,7 @@ func NewBcond(condition immediates.Condition) gen.InstructionDefinition { func (b Bcond) Target( info *gen.InstructionInfo, ) (*gen.LabelInfo, core.ResultList) { - results := aarch64translation.AssertArgumentsExactly(info, 1) + results := gen.AssertArgumentsExactly(info, 1) if !results.IsEmpty() { return nil, results } @@ -91,7 +91,7 @@ func (b Bcond) Validate( _, curResults := b.Target(info) results.Extend(&curResults) - curResults = aarch64translation.AssertTargetsExactly(info, 0) + curResults = gen.AssertTargetsExactly(info, 0) results.Extend(&curResults) if !results.IsEmpty() { diff --git a/aarch64/isa/bl.go b/aarch64/isa/bl.go index 50b3e1e..4f65368 100644 --- a/aarch64/isa/bl.go +++ b/aarch64/isa/bl.go @@ -25,7 +25,7 @@ func (b Bl) Operator(*gen.InstructionInfo) string { func (b Bl) Target( info *gen.InstructionInfo, ) (*gen.FunctionInfo, core.ResultList) { - results := aarch64translation.AssertArgumentsExactly(info, 1) + results := gen.AssertArgumentsExactly(info, 1) if !results.IsEmpty() { return nil, results } @@ -44,7 +44,7 @@ func (b Bl) Validate(info *gen.InstructionInfo) core.ResultList { _, curResults := b.Target(info) results.Extend(&curResults) - curResults = aarch64translation.AssertTargetsExactly(info, 0) + curResults = gen.AssertTargetsExactly(info, 0) results.Extend(&curResults) if !results.IsEmpty() { diff --git a/aarch64/isa/movz.go b/aarch64/isa/movz.go index 96b281e..2dd0e7c 100644 --- a/aarch64/isa/movz.go +++ b/aarch64/isa/movz.go @@ -23,7 +23,7 @@ func (Movz) Operator(*gen.InstructionInfo) string { } func (i Movz) Xd(info *gen.InstructionInfo) (registers.GPRegister, core.ResultList) { - results := aarch64translation.AssertTargetsExactly(info, 1) + results := gen.AssertTargetsExactly(info, 1) if !results.IsEmpty() { return registers.GPRegister(0), results @@ -40,7 +40,7 @@ func (i Movz) Xd(info *gen.InstructionInfo) (registers.GPRegister, core.ResultLi func (i Movz) Immediate( info *gen.InstructionInfo, ) (immediates.Immediate16, instructions.MovShift, core.ResultList) { - results := aarch64translation.AssertArgumentsBetween(info, 1, 2) + results := gen.AssertArgumentsBetween(info, 1, 2) if !results.IsEmpty() { return immediates.Immediate16(0), instructions.MovShift0, results diff --git a/aarch64/isa/ret.go b/aarch64/isa/ret.go index ad5628b..cb2b87b 100644 --- a/aarch64/isa/ret.go +++ b/aarch64/isa/ret.go @@ -36,7 +36,7 @@ func (i Ret) Codegen( } func (i Ret) Xn(info *gen.InstructionInfo) (registers.GPRegister, core.ResultList) { - results := aarch64translation.AssertArgumentsBetween(info, 0, 1) + results := gen.AssertArgumentsBetween(info, 0, 1) if !results.IsEmpty() { return registers.GPRegister(0), results } @@ -58,7 +58,7 @@ func (i Ret) Validate(info *gen.InstructionInfo) core.ResultList { _, curResults := i.Xn(info) results.Extend(&curResults) - curResults = aarch64translation.AssertTargetsExactly(info, 0) + curResults = gen.AssertTargetsExactly(info, 0) results.Extend(&curResults) if !results.IsEmpty() { diff --git a/aarch64/translation/arguments.go b/aarch64/translation/arguments.go index cac3b09..9d87bfc 100644 --- a/aarch64/translation/arguments.go +++ b/aarch64/translation/arguments.go @@ -1,9 +1,7 @@ package aarch64translation import ( - "fmt" "math/big" - "strconv" "alon.kr/x/aarch64codegen/immediates" "alon.kr/x/aarch64codegen/instructions" @@ -13,79 +11,6 @@ import ( "alon.kr/x/usm/gen" ) -func AssertAtLeastArguments( - info *gen.InstructionInfo, - atLeast int, -) core.ResultList { - if len(info.Arguments) < atLeast { - return list.FromSingle(core.Result{ - { - Type: core.ErrorResult, - Message: fmt.Sprintf("Expected at least %d arguments", atLeast), - Location: info.Declaration, - }, - }) - } - - return core.ResultList{} -} - -func AssertAtMostArguments( - info *gen.InstructionInfo, - atMost int, -) core.ResultList { - if len(info.Arguments) > atMost { - return list.FromSingle(core.Result{ - { - Type: core.ErrorResult, - Message: fmt.Sprintf("Expected at most %d arguments", atMost), - Location: info.Declaration, - }, - }) - } - - return core.ResultList{} -} - -func AssertArgumentsBetween( - info *gen.InstructionInfo, - atLeast int, - atMost int, -) core.ResultList { - if len(info.Arguments) < atLeast || len(info.Arguments) > atMost { - return list.FromSingle(core.Result{ - { - Type: core.ErrorResult, - Message: fmt.Sprintf( - "Expected between %d and %d arguments", - atLeast, - atMost, - ), - Location: info.Declaration, - }, - }) - } - - return core.ResultList{} -} - -func AssertArgumentsExactly( - info *gen.InstructionInfo, - count int, -) core.ResultList { - if len(info.Arguments) != count { - return list.FromSingle(core.Result{ - { - Type: core.ErrorResult, - Message: fmt.Sprintf("Expected %d arguments", count), - Location: info.Declaration, - }, - }) - } - - return core.ResultList{} -} - func ArgumentToAarch64GPRegister( argument gen.ArgumentInfo, ) (registers.GPRegister, core.ResultList) { @@ -222,46 +147,11 @@ fail: }) } -func AssertBigIntInSet( - view *core.UnmanagedSourceView, - bigInt *big.Int, - options []int64, -) (int64, core.ResultList) { - var value int64 - isInvalid := !bigInt.IsInt64() - - if isInvalid { - goto fail - } - - value = bigInt.Int64() - for _, option := range options { - if value == option { - return value, core.ResultList{} - } - } - -fail: - message := "Expected one of " - message += "#" + strconv.FormatInt(options[0], 10) - for _, option := range options { - message += ", #" + strconv.FormatInt(option, 10) - } - - return 0, list.FromSingle(core.Result{ - { - Type: core.ErrorResult, - Message: message, - Location: view, - }, - }) -} - func BigIntToAarch64MovShift( view *core.UnmanagedSourceView, bigInt *big.Int, ) (instructions.MovShift, core.ResultList) { - value, results := AssertBigIntInSet(view, bigInt, []int64{0, 16, 32, 48}) + value, results := gen.AssertBigIntInSet(view, bigInt, []int64{0, 16, 32, 48}) if !results.IsEmpty() { return 0, results } diff --git a/aarch64/translation/instructions.go b/aarch64/translation/instructions.go index 4ada212..ee22d5c 100644 --- a/aarch64/translation/instructions.go +++ b/aarch64/translation/instructions.go @@ -10,9 +10,9 @@ import ( func ValidateBinaryInstruction( info *gen.InstructionInfo, ) core.ResultList { - results := AssertTargetsExactly(info, 1) + results := gen.AssertTargetsExactly(info, 1) - argumentResults := AssertArgumentsExactly(info, 2) + argumentResults := gen.AssertArgumentsExactly(info, 2) results.Extend(&argumentResults) return results diff --git a/aarch64/translation/targets.go b/aarch64/translation/targets.go index 0de54ac..e3983c2 100644 --- a/aarch64/translation/targets.go +++ b/aarch64/translation/targets.go @@ -1,31 +1,11 @@ package aarch64translation import ( - "fmt" - "alon.kr/x/aarch64codegen/registers" - "alon.kr/x/list" "alon.kr/x/usm/core" "alon.kr/x/usm/gen" ) -func AssertTargetsExactly( - info *gen.InstructionInfo, - count int, -) core.ResultList { - if len(info.Targets) != count { - return list.FromSingle(core.Result{ - { - Type: core.ErrorResult, - Message: fmt.Sprintf("Expected %d targets", count), - Location: info.Declaration, - }, - }) - } - - return core.ResultList{} -} - func TargetToAarch64GPRegister( target *gen.TargetInfo, ) (registers.GPRegister, core.ResultList) { diff --git a/gen/assert.go b/gen/assert.go new file mode 100644 index 0000000..da16367 --- /dev/null +++ b/gen/assert.go @@ -0,0 +1,141 @@ +package gen + +import ( + "fmt" + "math/big" + "strconv" + + "alon.kr/x/list" + "alon.kr/x/usm/core" +) + +// MARK: Arguments + +func AssertAtLeastArguments( + info *InstructionInfo, + atLeast int, +) core.ResultList { + if len(info.Arguments) < atLeast { + return list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: fmt.Sprintf("Expected at least %d arguments", atLeast), + Location: info.Declaration, + }, + }) + } + + return core.ResultList{} +} + +func AssertAtMostArguments( + info *InstructionInfo, + atMost int, +) core.ResultList { + if len(info.Arguments) > atMost { + return list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: fmt.Sprintf("Expected at most %d arguments", atMost), + Location: info.Declaration, + }, + }) + } + + return core.ResultList{} +} + +func AssertArgumentsBetween( + info *InstructionInfo, + atLeast int, + atMost int, +) core.ResultList { + if len(info.Arguments) < atLeast || len(info.Arguments) > atMost { + return list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: fmt.Sprintf( + "Expected between %d and %d arguments", + atLeast, + atMost, + ), + Location: info.Declaration, + }, + }) + } + + return core.ResultList{} +} + +func AssertArgumentsExactly( + info *InstructionInfo, + count int, +) core.ResultList { + if len(info.Arguments) != count { + return list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: fmt.Sprintf("Expected %d arguments", count), + Location: info.Declaration, + }, + }) + } + + return core.ResultList{} +} + +// MARK: Targets + +func AssertTargetsExactly( + info *InstructionInfo, + count int, +) core.ResultList { + if len(info.Targets) != count { + return list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: fmt.Sprintf("Expected %d targets", count), + Location: info.Declaration, + }, + }) + } + + return core.ResultList{} +} + +// MARK: Integers + +func AssertBigIntInSet( + view *core.UnmanagedSourceView, + bigInt *big.Int, + options []int64, +) (int64, core.ResultList) { + var value int64 + isInvalid := !bigInt.IsInt64() + + if isInvalid { + goto fail + } + + value = bigInt.Int64() + for _, option := range options { + if value == option { + return value, core.ResultList{} + } + } + +fail: + message := "Expected one of " + message += "#" + strconv.FormatInt(options[0], 10) + for _, option := range options { + message += ", #" + strconv.FormatInt(option, 10) + } + + return 0, list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: message, + Location: view, + }, + }) +} From 51e1dbdbfb643cd0205c4d3360b808acdd71468f Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 12:44:00 +0300 Subject: [PATCH 15/33] Added `AssertArgumentIsTyped` --- gen/assert.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/gen/assert.go b/gen/assert.go index da16367..b6fb896 100644 --- a/gen/assert.go +++ b/gen/assert.go @@ -84,6 +84,28 @@ func AssertArgumentsExactly( return core.ResultList{} } +func AssertArgumentIsTyped( + arg ArgumentInfo, +) (ReferencedTypeInfo, core.ResultList) { + switch typedArg := arg.(type) { + + case *RegisterArgumentInfo: + return typedArg.Register.Type, core.ResultList{} + + case *ImmediateInfo: + return typedArg.Type, core.ResultList{} + + default: + return ReferencedTypeInfo{}, list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: "Expected an argument that can be typed", + Location: arg.Declaration(), + }, + }) + } +} + // MARK: Targets func AssertTargetsExactly( From 00cedb7ebe94e7367e2bbfa3576583ea7e47a39e Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 12:45:10 +0300 Subject: [PATCH 16/33] Added basic `usm` isa --- .gitignore | 1 - go.mod | 2 +- go.sum | 4 +-- justfile | 3 +- usm.go | 11 +++--- usm/isa/move.go | 66 ++++++++++++++++++++++++++++++++++++ usm/managers/context.go | 24 +++++++++++++ usm/managers/instructions.go | 16 +++++++++ usm/managers/registers.go | 42 +++++++++++++++++++++++ usm/managers/types.go | 35 +++++++++++++++++++ 10 files changed, 193 insertions(+), 11 deletions(-) create mode 100644 usm/isa/move.go create mode 100644 usm/managers/context.go create mode 100644 usm/managers/instructions.go create mode 100644 usm/managers/registers.go create mode 100644 usm/managers/types.go diff --git a/.gitignore b/.gitignore index 36d8760..336c342 100644 --- a/.gitignore +++ b/.gitignore @@ -27,7 +27,6 @@ # build files build -usm # coverage coverage.txt diff --git a/go.mod b/go.mod index 3c67536..3552939 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module alon.kr/x/usm go 1.23.0 require ( - alon.kr/x/aarch64codegen v0.0.0-20250423211537-52c2f85d1367 + alon.kr/x/aarch64codegen v0.0.0-20250509160800-a2358754df5d alon.kr/x/faststringmap v0.0.0-20250503134653-20d6364c2c94 alon.kr/x/graph v0.0.0-20250319212444-dd67d0281ab7 alon.kr/x/list v0.0.0-20241203223347-3173d76828c0 diff --git a/go.sum b/go.sum index 3c413ef..f328d52 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -alon.kr/x/aarch64codegen v0.0.0-20250423211537-52c2f85d1367 h1:l+WdERkNIO4GVBHvcqs0PJSELEkr3A0CGbwSTL3/6pE= -alon.kr/x/aarch64codegen v0.0.0-20250423211537-52c2f85d1367/go.mod h1:WAdZYqOdp9KwoBjWamrMDAphahR5oXkPSICj3+tLIyQ= +alon.kr/x/aarch64codegen v0.0.0-20250509160800-a2358754df5d h1:zaesIgOOypUXQT8zaOj68unauQP83nD/dT/gBlDobLQ= +alon.kr/x/aarch64codegen v0.0.0-20250509160800-a2358754df5d/go.mod h1:WAdZYqOdp9KwoBjWamrMDAphahR5oXkPSICj3+tLIyQ= alon.kr/x/faststringmap v0.0.0-20250503134653-20d6364c2c94 h1:EdG6bQh5IT5MZ76MY85/GK4Ma7zoJ4zcFqd2fpxK6NI= alon.kr/x/faststringmap v0.0.0-20250503134653-20d6364c2c94/go.mod h1:dPORoTvAFIehHRaI/lfJV3eT6PL5tY7fpAtxzz7rQVw= alon.kr/x/graph v0.0.0-20250319212444-dd67d0281ab7 h1:0d/oLvPQWc1B38ZfWfJ5UQXeHx9JT8gmp3K0AOOG8aQ= diff --git a/justfile b/justfile index 8766795..71b6797 100644 --- a/justfile +++ b/justfile @@ -4,9 +4,10 @@ GO := `if command -v richgo >/dev/null 2>&1; then echo richgo; else echo go; fi` PY := "python3" COVERPROFILE := "coverage.out" +OUTPUT := "usm.out" build: - {{GO}} build + {{GO}} build -o {{OUTPUT}} test: {{GO}} test ./... diff --git a/usm.go b/usm.go index 0c537ee..b6c0024 100644 --- a/usm.go +++ b/usm.go @@ -12,7 +12,7 @@ import ( "alon.kr/x/usm/lex" "alon.kr/x/usm/parse" "alon.kr/x/usm/transform" - usm64managers "alon.kr/x/usm/usm64/managers" + usmmanagers "alon.kr/x/usm/usm/managers" "github.com/spf13/cobra" ) @@ -21,7 +21,7 @@ var targets = transform.NewTargetCollection( Names: []string{"usm"}, Extensions: []string{".usm"}, Description: "A universal assembly language", - GenerationContext: usm64managers.NewGenerationContext(), + GenerationContext: usmmanagers.NewGenerationContext(), Transformations: *transform.NewTransformationCollection( &transform.Transformation{ Names: []string{"dead-code-elimination", "dce"}, @@ -31,7 +31,7 @@ var targets = transform.NewTargetCollection( }, &transform.Transformation{ Names: []string{"aarch64", "arm64"}, - Description: "Converts the universal assembly to matching machine specific aarch64 assembly", + Description: "Converts the universal assembly to matching machine specific AArch64 assembly", TargetName: "aarch64", // Transform: , }, @@ -41,7 +41,7 @@ var targets = transform.NewTargetCollection( &transform.Target{ Names: []string{"aarch64", "arm64"}, Extensions: []string{".aarch64.usm", ".arm64.usm"}, - Description: "Aarch64 (arm64v8) assembly language", + Description: "AArch64 (ARM64, ARMv8) assembly", GenerationContext: aarch64managers.NewGenerationContext(), Transformations: *transform.NewTransformationCollection( &transform.Transformation{ @@ -56,8 +56,7 @@ var targets = transform.NewTargetCollection( Names: []string{ "aarch64-macho-object", "aarch64-macho-obj", - "arm64-macho-object", - "arm64-macho-obj", + "aarch64-macho", }, Extensions: []string{".o"}, Description: "Mach-O object file containing aarch64 assembly", diff --git a/usm/isa/move.go b/usm/isa/move.go new file mode 100644 index 0000000..d69cf16 --- /dev/null +++ b/usm/isa/move.go @@ -0,0 +1,66 @@ +package usmisa + +import ( + "fmt" + + "alon.kr/x/list" + "alon.kr/x/usm/core" + "alon.kr/x/usm/gen" +) + +type Move struct { + gen.NonBranchingInstruction +} + +func NewMove() gen.InstructionDefinition { + return Move{} +} + +func (i Move) Operator(*gen.InstructionInfo) string { + return "" +} + +func (i Move) Validate(info *gen.InstructionInfo) core.ResultList { + results := core.ResultList{} + + curResults := gen.AssertTargetsExactly(info, 1) + results.Extend(&curResults) + + curResults = gen.AssertArgumentsExactly(info, 1) + results.Extend(&curResults) + + if !results.IsEmpty() { + return results + } + + argumentType, curResults := gen.AssertArgumentIsTyped(info.Arguments[0]) + results.Extend(&curResults) + + if !results.IsEmpty() { + return results + } + + targetType := info.Targets[0].Register.Type + + if !targetType.Equal(argumentType) { + return list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: "Target and argument types do not match", + Location: info.Declaration, + }, + { + Type: core.HintResult, + Message: fmt.Sprintf("Target type is \"%s\"", targetType), + Location: targetType.Declaration, + }, + { + Type: core.HintResult, + Message: fmt.Sprintf("Argument type is \"%s\"", argumentType), + Location: argumentType.Declaration, + }, + }) + } + + return core.ResultList{} +} diff --git a/usm/managers/context.go b/usm/managers/context.go new file mode 100644 index 0000000..26aa18f --- /dev/null +++ b/usm/managers/context.go @@ -0,0 +1,24 @@ +package usmmanagers + +import ( + "math/big" + + "alon.kr/x/usm/gen" +) + +func NewManagerCreators() gen.ManagerCreators { + return gen.ManagerCreators{ + RegisterManagerCreator: NewRegisterManager, + LabelManagerCreator: gen.NewLabelMap, + TypeManagerCreator: NewTypeManager, + GlobalManagerCreator: gen.NewGlobalMap, + } +} + +func NewGenerationContext() *gen.GenerationContext { + return &gen.GenerationContext{ + ManagerCreators: NewManagerCreators(), + Instructions: NewInstructionManager(), + PointerSize: big.NewInt(64), + } +} diff --git a/usm/managers/instructions.go b/usm/managers/instructions.go new file mode 100644 index 0000000..5011922 --- /dev/null +++ b/usm/managers/instructions.go @@ -0,0 +1,16 @@ +package usmmanagers + +import ( + "alon.kr/x/faststringmap" + "alon.kr/x/usm/gen" + usmisa "alon.kr/x/usm/usm/isa" +) + +func NewInstructionManager() gen.InstructionManager { + return gen.NewInstructionMap( + []faststringmap.MapEntry[gen.InstructionDefinition]{ + {Key: "", Value: usmisa.NewMove()}, + }, + false, + ) +} diff --git a/usm/managers/registers.go b/usm/managers/registers.go new file mode 100644 index 0000000..96a6fba --- /dev/null +++ b/usm/managers/registers.go @@ -0,0 +1,42 @@ +package usmmanagers + +import ( + "alon.kr/x/usm/core" + "alon.kr/x/usm/gen" +) + +type RegisterMap map[string]*gen.RegisterInfo + +func (m *RegisterMap) GetRegister(name string) *gen.RegisterInfo { + val, ok := (*m)[name] + if !ok { + return nil + } + return val +} + +func (m *RegisterMap) NewRegister(reg *gen.RegisterInfo) core.ResultList { + (*m)[reg.Name] = reg + return core.ResultList{} +} + +func (m *RegisterMap) DeleteRegister(reg *gen.RegisterInfo) core.ResultList { + delete(*m, reg.Name) + return core.ResultList{} +} + +func (m *RegisterMap) Size() int { + return len(*m) +} + +func (m *RegisterMap) GetAllRegisters() []*gen.RegisterInfo { + registers := make([]*gen.RegisterInfo, 0, len(*m)) + for _, reg := range *m { + registers = append(registers, reg) + } + return registers +} + +func NewRegisterManager(*gen.FileGenerationContext) gen.RegisterManager { + return &RegisterMap{} +} diff --git a/usm/managers/types.go b/usm/managers/types.go new file mode 100644 index 0000000..4862052 --- /dev/null +++ b/usm/managers/types.go @@ -0,0 +1,35 @@ +package usmmanagers + +import ( + "math/big" + + "alon.kr/x/usm/core" + "alon.kr/x/usm/gen" +) + +type TypeMap struct { + BaseType *gen.NamedTypeInfo +} + +func (m *TypeMap) GetType(name string) *gen.NamedTypeInfo { + if name == m.BaseType.Name { + return m.BaseType + } else { + return nil + } +} + +func (m *TypeMap) NewType(*gen.NamedTypeInfo) core.Result { + return core.Result{ + { + Type: core.ErrorResult, + Message: "Type declaration not supported in usm isa", + }, + } +} + +func NewTypeManager(*gen.GenerationContext) gen.TypeManager { + return &TypeMap{ + BaseType: gen.NewNamedTypeInfo("$64", big.NewInt(64), nil), + } +} From 768c326c4ad77b5e9c632baad4eb156ee8b2fdba Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 13:24:42 +0300 Subject: [PATCH 17/33] `usm` isa now supports all integer types --- usm/managers/types.go | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/usm/managers/types.go b/usm/managers/types.go index 4862052..0737764 100644 --- a/usm/managers/types.go +++ b/usm/managers/types.go @@ -7,29 +7,52 @@ import ( "alon.kr/x/usm/gen" ) -type TypeMap struct { - BaseType *gen.NamedTypeInfo +// In the USM isa, there are infinitly many "base types", of the form "$", +// each representing an integer of size n bits. +// We create those types lazily, and store them in the map. +type TypeMap map[string]*gen.NamedTypeInfo + +// Check if the provided type name is of the form "$" where n is a +// non-negative integer. +func (TypeMap) toBaseTypeSize(name string) *big.Int { + // notice that there are multiple strings that can be mapped to the + // same size, e.g. "$64" and "$064". + + size, ok := new(big.Int).SetString(name[1:], 10) + if !ok || size.Sign() < 0 { + return nil + } + + return size +} + +func (TypeMap) baseTypeSizeToCannonicalName(size *big.Int) string { + return "$" + size.String() } func (m *TypeMap) GetType(name string) *gen.NamedTypeInfo { - if name == m.BaseType.Name { - return m.BaseType - } else { - return nil + size := m.toBaseTypeSize(name) + if size != nil { + // is a base type, convert name to cannonical form + name = m.baseTypeSizeToCannonicalName(size) + if _, exists := (*m)[name]; !exists { + // if does not exist, create it! + (*m)[name] = gen.NewNamedTypeInfo(name, size, nil) + } } + + return (*m)[name] } func (m *TypeMap) NewType(*gen.NamedTypeInfo) core.Result { return core.Result{ { Type: core.ErrorResult, - Message: "Type declaration not supported in usm isa", + Message: "Type declaration not supported yet in usm isa", }, } } func NewTypeManager(*gen.GenerationContext) gen.TypeManager { - return &TypeMap{ - BaseType: gen.NewNamedTypeInfo("$64", big.NewInt(64), nil), - } + return &TypeMap{} } From 83b4d1442204a2e174416ef9abd956d8956f880c Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 13:24:59 +0300 Subject: [PATCH 18/33] Added `ret` instruction to `usm` isa --- gen/instruction_definition.go | 8 ++++++++ usm/isa/ret.go | 34 ++++++++++++++++++++++++++++++++++ usm/managers/instructions.go | 1 + 3 files changed, 43 insertions(+) create mode 100644 usm/isa/ret.go diff --git a/gen/instruction_definition.go b/gen/instruction_definition.go index b68ab2f..328268d 100644 --- a/gen/instruction_definition.go +++ b/gen/instruction_definition.go @@ -32,3 +32,11 @@ func (NonBranchingInstruction) PossibleNextSteps(*InstructionInfo) (StepInfo, co PossibleContinue: true, }, core.ResultList{} } + +type ReturningInstruction struct{} + +func (ReturningInstruction) PossibleNextSteps(*InstructionInfo) (StepInfo, core.ResultList) { + return StepInfo{ + PossibleReturn: true, + }, core.ResultList{} +} diff --git a/usm/isa/ret.go b/usm/isa/ret.go new file mode 100644 index 0000000..1ffe52b --- /dev/null +++ b/usm/isa/ret.go @@ -0,0 +1,34 @@ +package usmisa + +import ( + "alon.kr/x/usm/core" + "alon.kr/x/usm/gen" +) + +type Ret struct { + gen.ReturningInstruction +} + +func NewRet() gen.InstructionDefinition { + return Ret{} +} + +func (i Ret) Operator(*gen.InstructionInfo) string { + return "ret" +} + +func (i Ret) Validate(info *gen.InstructionInfo) core.ResultList { + results := core.ResultList{} + + curResults := gen.AssertTargetsExactly(info, 0) + results.Extend(&curResults) + + curResults = gen.AssertArgumentsExactly(info, 0) + results.Extend(&curResults) + + if !results.IsEmpty() { + return results + } + + return core.ResultList{} +} diff --git a/usm/managers/instructions.go b/usm/managers/instructions.go index 5011922..1f53a7f 100644 --- a/usm/managers/instructions.go +++ b/usm/managers/instructions.go @@ -10,6 +10,7 @@ func NewInstructionManager() gen.InstructionManager { return gen.NewInstructionMap( []faststringmap.MapEntry[gen.InstructionDefinition]{ {Key: "", Value: usmisa.NewMove()}, + {Key: "ret", Value: usmisa.NewRet()}, }, false, ) From 1e8b7f9a93aab3999c754815ac99877d6262fb95 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 13:30:41 +0300 Subject: [PATCH 19/33] Fixed `add` AArch64 generation tests --- aarch64/isa/add_test.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/aarch64/isa/add_test.go b/aarch64/isa/add_test.go index c68a292..12d37f2 100644 --- a/aarch64/isa/add_test.go +++ b/aarch64/isa/add_test.go @@ -22,7 +22,7 @@ func buildInstructionFromSource( t *testing.T, def gen.InstructionDefinition, src string, -) aarch64codegen.Instruction { +) (*gen.InstructionInfo, aarch64codegen.Instruction) { srcView := core.NewSourceView(src) tokenizer := lex.NewTokenizer() @@ -46,7 +46,7 @@ func buildInstructionFromSource( inst, ok := info.Instruction.(aarch64codegen.Instruction) assert.True(t, ok) - return inst + return info, inst } func assertExpectedCodegen( @@ -55,9 +55,12 @@ func assertExpectedCodegen( expected instructions.Instruction, src string, ) { - inst := buildInstructionFromSource(t, def, src) + info, inst := buildInstructionFromSource(t, def, src) + + generationContext := &aarch64codegen.InstructionCodegenContext{ + InstructionInfo: info, + } - generationContext := &aarch64codegen.InstructionCodegenContext{} code, results := inst.Codegen(generationContext) assert.True(t, results.IsEmpty()) From d4ccb1dbe2561f9815e75745fd29091ee99afbb2 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 16:20:36 +0300 Subject: [PATCH 20/33] Separated validation step from generation step --- aarch64/codegen/file.go | 7 +++-- gen/basic_block_info.go | 17 +++++++++++- gen/file_generator.go | 10 +++++-- gen/file_info.go | 51 +++++++++++++++++++++++++++++------ gen/function_info.go | 13 +++++++++ gen/instruction_definition.go | 4 +++ gen/instruction_generator.go | 6 ----- gen/instruction_info.go | 4 +++ 8 files changed, 93 insertions(+), 19 deletions(-) diff --git a/aarch64/codegen/file.go b/aarch64/codegen/file.go index ceb60fd..628f9d0 100644 --- a/aarch64/codegen/file.go +++ b/aarch64/codegen/file.go @@ -31,14 +31,17 @@ func NewFileCodegenContext(file *gen.FileInfo) *FileCodegenContext { functionIndices := make(map[*gen.FunctionInfo]uint32, len(file.Functions)) offset := uint64(0) - for idx, function := range file.Functions { + idx := uint32(0) + for _, function := range file.Functions { if function.IsDefined() { functionOffsets[function] = offset - functionIndices[function] = uint32(idx) + functionIndices[function] = idx functionSize := uint64(function.Size()) * 4 // TODO: handle overflow? offset += functionSize } + + idx++ } return &FileCodegenContext{ diff --git a/gen/basic_block_info.go b/gen/basic_block_info.go index 2873075..2d32c8d 100644 --- a/gen/basic_block_info.go +++ b/gen/basic_block_info.go @@ -1,6 +1,10 @@ package gen -import "slices" +import ( + "slices" + + "alon.kr/x/usm/core" +) type BasicBlockInfo struct { *FunctionInfo @@ -28,6 +32,17 @@ func NewEmptyBasicBlockInfo(function *FunctionInfo) *BasicBlockInfo { } } +func (b *BasicBlockInfo) Validate() core.ResultList { + results := core.ResultList{} + + for _, instruction := range b.Instructions { + curResults := instruction.Validate() + results.Extend(&curResults) + } + + return results +} + // Returns the number of instructions in the basic block. func (b *BasicBlockInfo) Size() int { return len(b.Instructions) diff --git a/gen/file_generator.go b/gen/file_generator.go index 24490d3..7e65696 100644 --- a/gen/file_generator.go +++ b/gen/file_generator.go @@ -86,8 +86,14 @@ func (g *FileGenerator) Generate( return nil, results } - file := &FileInfo{ - Functions: functions, + file := NewFileInfo() + for _, function := range functions { + file.AppendFunction(function) + } + + results = file.Validate() + if !results.IsEmpty() { + return nil, results } return file, core.ResultList{} diff --git a/gen/file_info.go b/gen/file_info.go index fc2ba77..9df4bf6 100644 --- a/gen/file_info.go +++ b/gen/file_info.go @@ -1,7 +1,15 @@ package gen +import "alon.kr/x/usm/core" + type FileInfo struct { - Functions []*FunctionInfo + Functions map[string]*FunctionInfo +} + +func NewFileInfo() *FileInfo { + return &FileInfo{ + Functions: make(map[string]*FunctionInfo), + } } func (i *FileInfo) String() string { @@ -11,11 +19,17 @@ func (i *FileInfo) String() string { return s } - for _, function := range i.Functions[:len(i.Functions)-1] { - s += function.String() + "\n" + functions := make([]*FunctionInfo, 0, len(i.Functions)) + for _, function := range i.Functions { + functions = append(functions, function) } - s += i.Functions[len(i.Functions)-1].String() + for _, f := range functions[:len(functions)-1] { + s += f.String() + "\n" + } + + lastFunction := functions[len(functions)-1] + s += lastFunction.String() return s } @@ -23,10 +37,31 @@ func (i *FileInfo) String() string { // GetFunction returns the function with the given name, or nil if it does not // exist. func (i *FileInfo) GetFunction(name string) *FunctionInfo { + info, ok := i.Functions[name] + if !ok { + return nil + } + + return info +} + +func (i *FileInfo) AppendFunction(function *FunctionInfo) { + oldFunction, ok := i.Functions[function.Name] + if ok { + oldFunction.FileInfo = nil + } + + function.FileInfo = i + i.Functions[function.Name] = function +} + +func (i *FileInfo) Validate() core.ResultList { + results := core.ResultList{} + for _, function := range i.Functions { - if function.Name == name { - return function - } + curResults := function.Validate() + results.Extend(&curResults) } - return nil + + return results } diff --git a/gen/function_info.go b/gen/function_info.go index 965d412..5fe4994 100644 --- a/gen/function_info.go +++ b/gen/function_info.go @@ -3,6 +3,8 @@ package gen import "alon.kr/x/usm/core" type FunctionInfo struct { + *FileInfo + Name string Declaration *core.UnmanagedSourceView @@ -73,3 +75,14 @@ func (i *FunctionInfo) String() string { s += "\n" return s } + +func (i *FunctionInfo) Validate() core.ResultList { + results := core.ResultList{} + + for _, block := range i.CollectBasicBlocks() { + curResults := block.Validate() + results.Extend(&curResults) + } + + return results +} diff --git a/gen/instruction_definition.go b/gen/instruction_definition.go index 328268d..741614b 100644 --- a/gen/instruction_definition.go +++ b/gen/instruction_definition.go @@ -22,6 +22,10 @@ type InstructionDefinition interface { // Validate the instruction information structure, according to the // expected arguments, targets, and other related information. + // + // Instruction validation should as one of the last steps in the compilation + // process, and you should be able to assume that all relevant information + // in the structures is filled in and propagated correctly. Validate(*InstructionInfo) core.ResultList } diff --git a/gen/instruction_generator.go b/gen/instruction_generator.go index 92b1872..4eb1a81 100644 --- a/gen/instruction_generator.go +++ b/gen/instruction_generator.go @@ -138,11 +138,5 @@ func (g *InstructionGenerator) Generate( instCtx.InstructionInfo.AppendArgument(arguments...) instCtx.InstructionInfo.SetInstruction(instDef) - - results = instDef.Validate(instCtx.InstructionInfo) - if !results.IsEmpty() { - return nil, results - } - return instCtx.InstructionInfo, core.ResultList{} } diff --git a/gen/instruction_info.go b/gen/instruction_info.go index 01aeace..37f91bb 100644 --- a/gen/instruction_info.go +++ b/gen/instruction_info.go @@ -33,6 +33,10 @@ func NewEmptyInstructionInfo( } } +func (i *InstructionInfo) Validate() core.ResultList { + return i.Instruction.Validate(i) +} + // Appends the given register(s) as a target(s) of the instruction, // including updating the required instruction and register information fields. func (i *InstructionInfo) AppendTarget(targets ...*TargetInfo) { From 937f72cbb48c138306d86e8ffdbd1a3eb05ede8f Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 16:25:13 +0300 Subject: [PATCH 21/33] Added `IsCritical` to existing usm instructions --- usm/isa/move.go | 4 ++++ usm/isa/ret.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/usm/isa/move.go b/usm/isa/move.go index d69cf16..fc49be7 100644 --- a/usm/isa/move.go +++ b/usm/isa/move.go @@ -20,6 +20,10 @@ func (i Move) Operator(*gen.InstructionInfo) string { return "" } +func (i Move) IsCritical(*gen.InstructionInfo) bool { + return false +} + func (i Move) Validate(info *gen.InstructionInfo) core.ResultList { results := core.ResultList{} diff --git a/usm/isa/ret.go b/usm/isa/ret.go index 1ffe52b..fb4c1d2 100644 --- a/usm/isa/ret.go +++ b/usm/isa/ret.go @@ -17,6 +17,10 @@ func (i Ret) Operator(*gen.InstructionInfo) string { return "ret" } +func (i Ret) IsCritical(*gen.InstructionInfo) bool { + return true +} + func (i Ret) Validate(info *gen.InstructionInfo) core.ResultList { results := core.ResultList{} From ae6c7bf5e92b998b03275155eb231cc286fceed2 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sat, 31 May 2025 16:25:33 +0300 Subject: [PATCH 22/33] Added `call` usm instruction --- usm/isa/call.go | 66 ++++++++++++++++++++++++++++++++++++ usm/managers/instructions.go | 1 + 2 files changed, 67 insertions(+) create mode 100644 usm/isa/call.go diff --git a/usm/isa/call.go b/usm/isa/call.go new file mode 100644 index 0000000..6fa55d7 --- /dev/null +++ b/usm/isa/call.go @@ -0,0 +1,66 @@ +package usmisa + +import ( + "alon.kr/x/list" + "alon.kr/x/usm/core" + "alon.kr/x/usm/gen" +) + +// The call instruction +type Call struct { + gen.NonBranchingInstruction +} + +func NewCall() gen.InstructionDefinition { + return Call{} +} + +func (i Call) Operator(*gen.InstructionInfo) string { + return "call" +} + +func (i Call) IsCritical(*gen.InstructionInfo) bool { + return true +} + +func (i Call) Validate(info *gen.InstructionInfo) core.ResultList { + results := gen.AssertAtLeastArguments(info, 1) + if !results.IsEmpty() { + return results + } + + funcArg, ok := info.Arguments[0].(*gen.GlobalArgumentInfo) + if !ok { + return list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: "Call instruction requires a global function argument as the first argument", + Location: funcArg.Declaration(), + }, + }) + } + + funcInfo := info.FileInfo.GetFunction(funcArg.Name()) + if funcInfo == nil { + return list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: "Call global function does not exist, or is not a function", + Location: funcArg.Declaration(), + }, + }) + } + + // TODO: this should also check types. + curResults := gen.AssertArgumentsExactly(info, len(funcInfo.Parameters)+1) + results.Extend(&curResults) + + curResults = gen.AssertTargetsExactly(info, len(funcInfo.Targets)) + results.Extend(&curResults) + + if !results.IsEmpty() { + return results + } + + return core.ResultList{} +} diff --git a/usm/managers/instructions.go b/usm/managers/instructions.go index 1f53a7f..f988d9c 100644 --- a/usm/managers/instructions.go +++ b/usm/managers/instructions.go @@ -11,6 +11,7 @@ func NewInstructionManager() gen.InstructionManager { []faststringmap.MapEntry[gen.InstructionDefinition]{ {Key: "", Value: usmisa.NewMove()}, {Key: "ret", Value: usmisa.NewRet()}, + {Key: "call", Value: usmisa.NewCall()}, }, false, ) From 035e55b7a60e9187c6bd7c919e153f621c06e347 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sun, 1 Jun 2025 11:06:50 +0300 Subject: [PATCH 23/33] Fixed DCE tests --- opt/testdata/dead_code_elimination/test1.usm | 10 ++++++---- opt/testdata/dead_code_elimination/test2.usm | 18 ++++++++++-------- opt/testdata/dead_code_elimination/test3.usm | 12 ++++++++++++ 3 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 opt/testdata/dead_code_elimination/test3.usm diff --git a/opt/testdata/dead_code_elimination/test1.usm b/opt/testdata/dead_code_elimination/test1.usm index c707f2e..67a52bd 100644 --- a/opt/testdata/dead_code_elimination/test1.usm +++ b/opt/testdata/dead_code_elimination/test1.usm @@ -1,14 +1,16 @@ +func @use $64 %reg + func @input { .entry $64 %a = $64 #0 $64 %b = $64 #1 - PUT %a - TERM + call @use %a + ret } func @expected { .entry $64 %a = $64 #0 - PUT %a - TERM + call @use %a + ret } diff --git a/opt/testdata/dead_code_elimination/test2.usm b/opt/testdata/dead_code_elimination/test2.usm index 04198bf..9943332 100644 --- a/opt/testdata/dead_code_elimination/test2.usm +++ b/opt/testdata/dead_code_elimination/test2.usm @@ -1,17 +1,19 @@ +func @use $64 %reg + func @input { .entry $64 %a = $64 #0 - $64 %b = ADD %a $64 #1 - $64 %c = ADD %b $64 #1 - PUT %c - TERM + $64 %b = %a + $64 %c = %b + call @use %c + ret } func @expected { .entry $64 %a = $64 #0 - $64 %b = ADD %a $64 #1 - $64 %c = ADD %b $64 #1 - PUT %c - TERM + $64 %b = %a + $64 %c = %b + call @use %c + ret } diff --git a/opt/testdata/dead_code_elimination/test3.usm b/opt/testdata/dead_code_elimination/test3.usm new file mode 100644 index 0000000..427905e --- /dev/null +++ b/opt/testdata/dead_code_elimination/test3.usm @@ -0,0 +1,12 @@ +func @input { +.entry + $64 %a = $64 #0 + $64 %b = %a + $64 %c = %b + ret +} + +func @expected { +.entry + ret +} From d62027997f82f028b8d5fbf5c32c53ac57a13d48 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Sun, 1 Jun 2025 11:09:36 +0300 Subject: [PATCH 24/33] DCE optimization now supported --- gen/translate.go | 27 +++++++++++++++++++++++++++ opt/dead_code_elimination.go | 36 ++++++++++++++++++++++++++++++------ opt/opt_test.go | 4 ++-- usm/isa/call.go | 21 +++++++++++++++------ usm/isa/move.go | 21 +++++++++++++++------ usm/isa/ret.go | 23 +++++++++++++++++------ 6 files changed, 106 insertions(+), 26 deletions(-) create mode 100644 gen/translate.go diff --git a/gen/translate.go b/gen/translate.go new file mode 100644 index 0000000..214ada4 --- /dev/null +++ b/gen/translate.go @@ -0,0 +1,27 @@ +package gen + +func ArgumentsToRegisters( + arguments []ArgumentInfo, +) []*RegisterInfo { + registers := []*RegisterInfo{} + + for _, arg := range arguments { + if regArg, ok := arg.(*RegisterArgumentInfo); ok { + registers = append(registers, regArg.Register) + } + } + + return registers +} + +func TargetsToRegisters( + targets []*TargetInfo, +) []*RegisterInfo { + registers := []*RegisterInfo{} + + for _, target := range targets { + registers = append(registers, target.Register) + } + + return registers +} diff --git a/opt/dead_code_elimination.go b/opt/dead_code_elimination.go index d6f3805..1e90078 100644 --- a/opt/dead_code_elimination.go +++ b/opt/dead_code_elimination.go @@ -21,20 +21,20 @@ import ( // instruction since we assume SSA form). type DCESupportedInstruction interface { - gen.BaseInstruction + gen.InstructionDefinition // Returns true if the instruction is a critical instruction, which means // it can't be removed by the dead code elimination process, by definition. // // A critical instruction might be a function call, a branch, an instruction // with a side effect, etc. - IsCritical() bool + IsCritical(info *gen.InstructionInfo) bool // Returns the registers that the instruction defines, directly or indirectly. - Defines() []*gen.RegisterInfo + Defines(info *gen.InstructionInfo) []*gen.RegisterInfo // Returns the registers that the instruction uses, directly or indirectly. - Uses() []*gen.RegisterInfo + Uses(info *gen.InstructionInfo) []*gen.RegisterInfo } func newDCENotSupportedError(instruction *gen.InstructionInfo) core.ResultList { @@ -45,6 +45,30 @@ func newDCENotSupportedError(instruction *gen.InstructionInfo) core.ResultList { }}) } +type CriticalInstruction struct{} + +func (CriticalInstruction) IsCritical(*gen.InstructionInfo) bool { + return true +} + +type NonCriticalInstruction struct{} + +func (NonCriticalInstruction) IsCritical(*gen.InstructionInfo) bool { + return false +} + +type UsesArgumentsInstruction struct{} + +func (UsesArgumentsInstruction) Uses(info *gen.InstructionInfo) []*gen.RegisterInfo { + return gen.ArgumentsToRegisters(info.Arguments) +} + +type DefinesTargetsInstruction struct{} + +func (DefinesTargetsInstruction) Defines(info *gen.InstructionInfo) []*gen.RegisterInfo { + return gen.TargetsToRegisters(info.Targets) +} + func collectCriticalInstructions( function *gen.FunctionInfo, ) (stack.Stack[*gen.InstructionInfo], core.ResultList) { @@ -57,7 +81,7 @@ func collectCriticalInstructions( if !ok { curResults := newDCENotSupportedError(instruction) results.Extend(&curResults) - } else if dceInstruction.IsCritical() { + } else if dceInstruction.IsCritical(instruction) { collected.Push(instruction) } } @@ -91,7 +115,7 @@ func collectUsefulInstructions( usefulInstructions.Add(instruction) - for _, register := range dceInstruction.Uses() { + for _, register := range dceInstruction.Uses(instruction) { definitions := register.Definitions // In SSA form, a register can have at most one definition, and thus diff --git a/opt/opt_test.go b/opt/opt_test.go index 22a3459..840bdbb 100644 --- a/opt/opt_test.go +++ b/opt/opt_test.go @@ -10,7 +10,7 @@ import ( "alon.kr/x/usm/gen" "alon.kr/x/usm/lex" "alon.kr/x/usm/parse" - usm64managers "alon.kr/x/usm/usm64/managers" + usmmanagers "alon.kr/x/usm/usm/managers" "github.com/stretchr/testify/assert" ) @@ -72,7 +72,7 @@ func generateFileInfo(t *testing.T, source string) *gen.FileInfo { fileNode, result := parse.NewFileParser().Parse(&tknView) assert.Nil(t, result) - ctx := usm64managers.NewGenerationContext() + ctx := usmmanagers.NewGenerationContext() generator := gen.NewFileGenerator() info, results := generator.Generate(ctx, srcView.Ctx(), fileNode) assert.True(t, results.IsEmpty(), "Failed to generate file info") diff --git a/usm/isa/call.go b/usm/isa/call.go index 6fa55d7..1d4745d 100644 --- a/usm/isa/call.go +++ b/usm/isa/call.go @@ -4,26 +4,27 @@ import ( "alon.kr/x/list" "alon.kr/x/usm/core" "alon.kr/x/usm/gen" + "alon.kr/x/usm/opt" ) // The call instruction type Call struct { gen.NonBranchingInstruction + + opt.CriticalInstruction + opt.UsesArgumentsInstruction + opt.DefinesTargetsInstruction } func NewCall() gen.InstructionDefinition { return Call{} } -func (i Call) Operator(*gen.InstructionInfo) string { +func (Call) Operator(*gen.InstructionInfo) string { return "call" } -func (i Call) IsCritical(*gen.InstructionInfo) bool { - return true -} - -func (i Call) Validate(info *gen.InstructionInfo) core.ResultList { +func (Call) Validate(info *gen.InstructionInfo) core.ResultList { results := gen.AssertAtLeastArguments(info, 1) if !results.IsEmpty() { return results @@ -64,3 +65,11 @@ func (i Call) Validate(info *gen.InstructionInfo) core.ResultList { return core.ResultList{} } + +func (Call) Defines(info *gen.InstructionInfo) []*gen.RegisterInfo { + return gen.TargetsToRegisters(info.Targets) +} + +func (Call) Uses(info *gen.InstructionInfo) []*gen.RegisterInfo { + return gen.ArgumentsToRegisters(info.Arguments) +} diff --git a/usm/isa/move.go b/usm/isa/move.go index fc49be7..0e943a3 100644 --- a/usm/isa/move.go +++ b/usm/isa/move.go @@ -6,25 +6,26 @@ import ( "alon.kr/x/list" "alon.kr/x/usm/core" "alon.kr/x/usm/gen" + "alon.kr/x/usm/opt" ) type Move struct { gen.NonBranchingInstruction + + opt.NonCriticalInstruction + opt.UsesArgumentsInstruction + opt.DefinesTargetsInstruction } func NewMove() gen.InstructionDefinition { return Move{} } -func (i Move) Operator(*gen.InstructionInfo) string { +func (Move) Operator(*gen.InstructionInfo) string { return "" } -func (i Move) IsCritical(*gen.InstructionInfo) bool { - return false -} - -func (i Move) Validate(info *gen.InstructionInfo) core.ResultList { +func (Move) Validate(info *gen.InstructionInfo) core.ResultList { results := core.ResultList{} curResults := gen.AssertTargetsExactly(info, 1) @@ -68,3 +69,11 @@ func (i Move) Validate(info *gen.InstructionInfo) core.ResultList { return core.ResultList{} } + +func (Move) Defines(info *gen.InstructionInfo) []*gen.RegisterInfo { + return gen.TargetsToRegisters(info.Targets) +} + +func (Move) Uses(info *gen.InstructionInfo) []*gen.RegisterInfo { + return gen.ArgumentsToRegisters(info.Arguments) +} diff --git a/usm/isa/ret.go b/usm/isa/ret.go index fb4c1d2..15075be 100644 --- a/usm/isa/ret.go +++ b/usm/isa/ret.go @@ -3,30 +3,33 @@ package usmisa import ( "alon.kr/x/usm/core" "alon.kr/x/usm/gen" + "alon.kr/x/usm/opt" ) type Ret struct { gen.ReturningInstruction + + opt.CriticalInstruction + opt.UsesArgumentsInstruction + opt.DefinesTargetsInstruction } func NewRet() gen.InstructionDefinition { return Ret{} } -func (i Ret) Operator(*gen.InstructionInfo) string { +func (Ret) Operator(*gen.InstructionInfo) string { return "ret" } -func (i Ret) IsCritical(*gen.InstructionInfo) bool { - return true -} - -func (i Ret) Validate(info *gen.InstructionInfo) core.ResultList { +func (Ret) Validate(info *gen.InstructionInfo) core.ResultList { results := core.ResultList{} curResults := gen.AssertTargetsExactly(info, 0) results.Extend(&curResults) + // TODO: this is not exactly correct, arguments to the ret instruction must + // match the targets of the function. curResults = gen.AssertArgumentsExactly(info, 0) results.Extend(&curResults) @@ -36,3 +39,11 @@ func (i Ret) Validate(info *gen.InstructionInfo) core.ResultList { return core.ResultList{} } + +func (Ret) Defines(info *gen.InstructionInfo) []*gen.RegisterInfo { + return []*gen.RegisterInfo{} +} + +func (Ret) Uses(info *gen.InstructionInfo) []*gen.RegisterInfo { + return gen.ArgumentsToRegisters(info.Arguments) +} From 42a88916ec9c4b76cb1248ad8d031a5e16128bb2 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Mon, 2 Jun 2025 14:13:20 +0300 Subject: [PATCH 25/33] Renamed `ArgumentToType` --- gen/assert.go | 22 ---------------------- gen/translate.go | 39 +++++++++++++++++++++++++++++++++++++++ usm/isa/move.go | 2 +- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/gen/assert.go b/gen/assert.go index b6fb896..da16367 100644 --- a/gen/assert.go +++ b/gen/assert.go @@ -84,28 +84,6 @@ func AssertArgumentsExactly( return core.ResultList{} } -func AssertArgumentIsTyped( - arg ArgumentInfo, -) (ReferencedTypeInfo, core.ResultList) { - switch typedArg := arg.(type) { - - case *RegisterArgumentInfo: - return typedArg.Register.Type, core.ResultList{} - - case *ImmediateInfo: - return typedArg.Type, core.ResultList{} - - default: - return ReferencedTypeInfo{}, list.FromSingle(core.Result{ - { - Type: core.ErrorResult, - Message: "Expected an argument that can be typed", - Location: arg.Declaration(), - }, - }) - } -} - // MARK: Targets func AssertTargetsExactly( diff --git a/gen/translate.go b/gen/translate.go index 214ada4..fb39a14 100644 --- a/gen/translate.go +++ b/gen/translate.go @@ -1,5 +1,10 @@ package gen +import ( + "alon.kr/x/list" + "alon.kr/x/usm/core" +) + func ArgumentsToRegisters( arguments []ArgumentInfo, ) []*RegisterInfo { @@ -14,6 +19,40 @@ func ArgumentsToRegisters( return registers } +func ArgumentToType(arg ArgumentInfo) (ReferencedTypeInfo, core.ResultList) { + switch typedArg := arg.(type) { + + case *RegisterArgumentInfo: + return typedArg.Register.Type, core.ResultList{} + + case *ImmediateInfo: + return typedArg.Type, core.ResultList{} + + default: + return ReferencedTypeInfo{}, list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: "Expected an argument that can be typed", + Location: arg.Declaration(), + }, + }) + } +} + +func ArgumentToLabel(arg ArgumentInfo) (*LabelInfo, core.ResultList) { + if labelArg, ok := arg.(*LabelArgumentInfo); ok { + return labelArg.Label, core.ResultList{} + } + + return nil, list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: "Expected a label argument", + Location: arg.Declaration(), + }, + }) +} + func TargetsToRegisters( targets []*TargetInfo, ) []*RegisterInfo { diff --git a/usm/isa/move.go b/usm/isa/move.go index 0e943a3..d83d222 100644 --- a/usm/isa/move.go +++ b/usm/isa/move.go @@ -38,7 +38,7 @@ func (Move) Validate(info *gen.InstructionInfo) core.ResultList { return results } - argumentType, curResults := gen.AssertArgumentIsTyped(info.Arguments[0]) + argumentType, curResults := gen.ArgumentToType(info.Arguments[0]) results.Extend(&curResults) if !results.IsEmpty() { From 64671ea9f89455f999992a1b554ca2d1c4384c55 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Mon, 2 Jun 2025 15:02:38 +0300 Subject: [PATCH 26/33] Added the `jz` instruction to `usm` ISA --- gen/instruction_definition.go | 9 ++++++ gen/translate.go | 12 ++++++++ opt/dead_code_elimination.go | 6 ++++ usm/isa/call.go | 2 ++ usm/isa/jz.go | 53 +++++++++++++++++++++++++++++++++++ usm/isa/move.go | 2 ++ usm/isa/ret.go | 12 ++------ usm/managers/instructions.go | 3 ++ 8 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 usm/isa/jz.go diff --git a/gen/instruction_definition.go b/gen/instruction_definition.go index 741614b..05b8094 100644 --- a/gen/instruction_definition.go +++ b/gen/instruction_definition.go @@ -37,6 +37,15 @@ func (NonBranchingInstruction) PossibleNextSteps(*InstructionInfo) (StepInfo, co }, core.ResultList{} } +type BranchesToLabelArgumentsOrContinues struct{} + +func (BranchesToLabelArgumentsOrContinues) PossibleNextSteps(info *InstructionInfo) (StepInfo, core.ResultList) { + return StepInfo{ + PossibleBranches: ArgumentsToLabels(info.Arguments), + PossibleContinue: true, + }, core.ResultList{} +} + type ReturningInstruction struct{} func (ReturningInstruction) PossibleNextSteps(*InstructionInfo) (StepInfo, core.ResultList) { diff --git a/gen/translate.go b/gen/translate.go index fb39a14..0379331 100644 --- a/gen/translate.go +++ b/gen/translate.go @@ -19,6 +19,18 @@ func ArgumentsToRegisters( return registers } +func ArgumentsToLabels(arguments []ArgumentInfo) []*LabelInfo { + labels := []*LabelInfo{} + + for _, arg := range arguments { + if labelArg, ok := arg.(*LabelArgumentInfo); ok { + labels = append(labels, labelArg.Label) + } + } + + return labels +} + func ArgumentToType(arg ArgumentInfo) (ReferencedTypeInfo, core.ResultList) { switch typedArg := arg.(type) { diff --git a/opt/dead_code_elimination.go b/opt/dead_code_elimination.go index 1e90078..785dd51 100644 --- a/opt/dead_code_elimination.go +++ b/opt/dead_code_elimination.go @@ -69,6 +69,12 @@ func (DefinesTargetsInstruction) Defines(info *gen.InstructionInfo) []*gen.Regis return gen.TargetsToRegisters(info.Targets) } +type DefinesNothingInstruction struct{} + +func (DefinesNothingInstruction) Defines(*gen.InstructionInfo) []*gen.RegisterInfo { + return []*gen.RegisterInfo{} +} + func collectCriticalInstructions( function *gen.FunctionInfo, ) (stack.Stack[*gen.InstructionInfo], core.ResultList) { diff --git a/usm/isa/call.go b/usm/isa/call.go index 1d4745d..5243243 100644 --- a/usm/isa/call.go +++ b/usm/isa/call.go @@ -9,8 +9,10 @@ import ( // The call instruction type Call struct { + // Control Flow gen.NonBranchingInstruction + // Dead Code Elimination opt.CriticalInstruction opt.UsesArgumentsInstruction opt.DefinesTargetsInstruction diff --git a/usm/isa/jz.go b/usm/isa/jz.go new file mode 100644 index 0000000..bb4ecec --- /dev/null +++ b/usm/isa/jz.go @@ -0,0 +1,53 @@ +package usmisa + +import ( + "alon.kr/x/usm/core" + "alon.kr/x/usm/gen" + "alon.kr/x/usm/opt" +) + +type Jz struct { + // Control Flow + gen.BranchesToLabelArgumentsOrContinues + + // Dead Code Elimination + opt.CriticalInstruction + opt.UsesArgumentsInstruction + opt.DefinesNothingInstruction +} + +func NewJz() gen.InstructionDefinition { + return Jz{} +} + +func (Jz) Operator(*gen.InstructionInfo) string { + return "jz" +} + +func (Jz) Validate(info *gen.InstructionInfo) core.ResultList { + results := core.ResultList{} + + curResults := gen.AssertTargetsExactly(info, 0) + results.Extend(&curResults) + + curResults = gen.AssertArgumentsExactly(info, 2) + results.Extend(&curResults) + + if !results.IsEmpty() { + return results + } + + valueArg := info.Arguments[0] + _, curResults = gen.ArgumentToType(valueArg) + results.Extend(&curResults) + + labelArg := info.Arguments[1] + _, curResults = gen.ArgumentToLabel(labelArg) + results.Extend(&curResults) + + if !results.IsEmpty() { + return results + } + + return core.ResultList{} +} diff --git a/usm/isa/move.go b/usm/isa/move.go index d83d222..c66bb9b 100644 --- a/usm/isa/move.go +++ b/usm/isa/move.go @@ -10,8 +10,10 @@ import ( ) type Move struct { + // Control Flow gen.NonBranchingInstruction + // Dead Code Elimination opt.NonCriticalInstruction opt.UsesArgumentsInstruction opt.DefinesTargetsInstruction diff --git a/usm/isa/ret.go b/usm/isa/ret.go index 15075be..2113cbe 100644 --- a/usm/isa/ret.go +++ b/usm/isa/ret.go @@ -7,11 +7,13 @@ import ( ) type Ret struct { + // Control Flow gen.ReturningInstruction + // Dead Code Elimination opt.CriticalInstruction opt.UsesArgumentsInstruction - opt.DefinesTargetsInstruction + opt.DefinesNothingInstruction } func NewRet() gen.InstructionDefinition { @@ -39,11 +41,3 @@ func (Ret) Validate(info *gen.InstructionInfo) core.ResultList { return core.ResultList{} } - -func (Ret) Defines(info *gen.InstructionInfo) []*gen.RegisterInfo { - return []*gen.RegisterInfo{} -} - -func (Ret) Uses(info *gen.InstructionInfo) []*gen.RegisterInfo { - return gen.ArgumentsToRegisters(info.Arguments) -} diff --git a/usm/managers/instructions.go b/usm/managers/instructions.go index f988d9c..73b7a40 100644 --- a/usm/managers/instructions.go +++ b/usm/managers/instructions.go @@ -10,8 +10,11 @@ func NewInstructionManager() gen.InstructionManager { return gen.NewInstructionMap( []faststringmap.MapEntry[gen.InstructionDefinition]{ {Key: "", Value: usmisa.NewMove()}, + + // Control Flow {Key: "ret", Value: usmisa.NewRet()}, {Key: "call", Value: usmisa.NewCall()}, + {Key: "jz", Value: usmisa.NewJz()}, }, false, ) From f49bab8c5362a91ad148531c10d50bd94335b3fa Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Mon, 2 Jun 2025 15:11:40 +0300 Subject: [PATCH 27/33] Fixed error message --- gen/translate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gen/translate.go b/gen/translate.go index 0379331..75e9f53 100644 --- a/gen/translate.go +++ b/gen/translate.go @@ -44,7 +44,7 @@ func ArgumentToType(arg ArgumentInfo) (ReferencedTypeInfo, core.ResultList) { return ReferencedTypeInfo{}, list.FromSingle(core.Result{ { Type: core.ErrorResult, - Message: "Expected an argument that can be typed", + Message: "Expected a typed argument", Location: arg.Declaration(), }, }) From 962e24fdd8c501ad04c098f2572809265d959c8c Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Mon, 2 Jun 2025 15:12:18 +0300 Subject: [PATCH 28/33] Added `phi` instruction to `usm` ISA --- usm/isa/phi.go | 102 +++++++++++++++++++++++++++++++++++ usm/managers/instructions.go | 3 ++ 2 files changed, 105 insertions(+) create mode 100644 usm/isa/phi.go diff --git a/usm/isa/phi.go b/usm/isa/phi.go new file mode 100644 index 0000000..8d0219f --- /dev/null +++ b/usm/isa/phi.go @@ -0,0 +1,102 @@ +package usmisa + +import ( + "alon.kr/x/list" + "alon.kr/x/set" + "alon.kr/x/usm/core" + "alon.kr/x/usm/gen" + "alon.kr/x/usm/opt" +) + +type Phi struct { + // Control Flow + gen.NonBranchingInstruction + + // Dead Code Elimination + opt.NonCriticalInstruction + opt.UsesArgumentsInstruction + opt.DefinesTargetsInstruction +} + +func NewPhi() gen.InstructionDefinition { + return Phi{} +} + +func (Phi) Operator(*gen.InstructionInfo) string { + return "phi" +} + +func (Phi) validateEvenArguments(info *gen.InstructionInfo) core.ResultList { + if len(info.Arguments)%2 != 0 { + return list.FromSingle(core.Result{ + { + Type: core.ErrorResult, + Message: "A \"phi\" instruction must have an even number of arguments.", + Location: info.Declaration, + }, + { + Type: core.HintResult, + Message: "Each pair of arguments should consist of a label and a value.", + }, + }) + } + + return core.ResultList{} +} + +func (i Phi) Validate(info *gen.InstructionInfo) core.ResultList { + results := core.ResultList{} + + curResults := gen.AssertTargetsExactly(info, 1) + results.Extend(&curResults) + + curResults = i.validateEvenArguments(info) + results.Extend(&curResults) + + if !results.IsEmpty() { + return results + } + + targetType := info.Targets[0].Register.Type + incomingEdges := set.FromSlice(info.BasicBlockInfo.BackwardEdges) + + for i := 0; i < len(info.Arguments); i += 2 { + labelArg := info.Arguments[i] + + label, curResults := gen.ArgumentToLabel(labelArg) + results.Extend(&curResults) + + valueArg := info.Arguments[i+1] + argType, curResults := gen.ArgumentToType(valueArg) + results.Extend(&curResults) + + if !results.IsEmpty() { + continue + } + + block := label.BasicBlock + if incomingEdges.Contains(block) { + incomingEdges.Remove(block) + } else { + results.Append(core.Result{ + { + Type: core.ErrorResult, + Message: "Label does not match any incoming edge, or appears multiple times.", + Location: labelArg.Declaration(), + }, + }) + } + + if !argType.Equal(targetType) { + results.Append(core.Result{ + { + Type: core.ErrorResult, + Message: "Argument type does not match target type.", + Location: valueArg.Declaration(), + }, + }) + } + } + + return results +} diff --git a/usm/managers/instructions.go b/usm/managers/instructions.go index 73b7a40..9bc265a 100644 --- a/usm/managers/instructions.go +++ b/usm/managers/instructions.go @@ -15,6 +15,9 @@ func NewInstructionManager() gen.InstructionManager { {Key: "ret", Value: usmisa.NewRet()}, {Key: "call", Value: usmisa.NewCall()}, {Key: "jz", Value: usmisa.NewJz()}, + + // Static Single Assignment (SSA) + {Key: "phi", Value: usmisa.NewPhi()}, }, false, ) From e8b9608a3675ea0239e67fff7abdbc1e645c9831 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Mon, 2 Jun 2025 15:19:26 +0300 Subject: [PATCH 29/33] Added `j` instruction to `usm` ISA --- gen/instruction_definition.go | 8 ++++++ opt/dead_code_elimination.go | 6 +++++ usm/isa/j.go | 49 +++++++++++++++++++++++++++++++++++ usm/managers/instructions.go | 5 +++- 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 usm/isa/j.go diff --git a/gen/instruction_definition.go b/gen/instruction_definition.go index 05b8094..d079838 100644 --- a/gen/instruction_definition.go +++ b/gen/instruction_definition.go @@ -37,6 +37,14 @@ func (NonBranchingInstruction) PossibleNextSteps(*InstructionInfo) (StepInfo, co }, core.ResultList{} } +type BranchToLabelArguments struct{} + +func (BranchToLabelArguments) PossibleNextSteps(info *InstructionInfo) (StepInfo, core.ResultList) { + return StepInfo{ + PossibleBranches: ArgumentsToLabels(info.Arguments), + }, core.ResultList{} +} + type BranchesToLabelArgumentsOrContinues struct{} func (BranchesToLabelArgumentsOrContinues) PossibleNextSteps(info *InstructionInfo) (StepInfo, core.ResultList) { diff --git a/opt/dead_code_elimination.go b/opt/dead_code_elimination.go index 785dd51..6c6dd54 100644 --- a/opt/dead_code_elimination.go +++ b/opt/dead_code_elimination.go @@ -63,6 +63,12 @@ func (UsesArgumentsInstruction) Uses(info *gen.InstructionInfo) []*gen.RegisterI return gen.ArgumentsToRegisters(info.Arguments) } +type UsesNothingInstruction struct{} + +func (UsesNothingInstruction) Uses(*gen.InstructionInfo) []*gen.RegisterInfo { + return []*gen.RegisterInfo{} +} + type DefinesTargetsInstruction struct{} func (DefinesTargetsInstruction) Defines(info *gen.InstructionInfo) []*gen.RegisterInfo { diff --git a/usm/isa/j.go b/usm/isa/j.go new file mode 100644 index 0000000..2b507f5 --- /dev/null +++ b/usm/isa/j.go @@ -0,0 +1,49 @@ +package usmisa + +import ( + "alon.kr/x/usm/core" + "alon.kr/x/usm/gen" + "alon.kr/x/usm/opt" +) + +type J struct { + // Control Flow + gen.BranchToLabelArguments + + // Dead Code Elimination + opt.CriticalInstruction + opt.UsesNothingInstruction + opt.DefinesNothingInstruction +} + +func NewJump() J { + return J{} +} + +func (J) Operator(*gen.InstructionInfo) string { + return "j" +} + +func (i J) Validate(info *gen.InstructionInfo) core.ResultList { + results := core.ResultList{} + + curResults := gen.AssertTargetsExactly(info, 0) + results.Extend(&curResults) + + curResults = gen.AssertArgumentsExactly(info, 1) + results.Extend(&curResults) + + if !results.IsEmpty() { + return results + } + + labelArg := info.Arguments[0] + _, curResults = gen.ArgumentToLabel(labelArg) + results.Extend(&curResults) + + if !results.IsEmpty() { + return results + } + + return core.ResultList{} +} diff --git a/usm/managers/instructions.go b/usm/managers/instructions.go index 9bc265a..782dd9b 100644 --- a/usm/managers/instructions.go +++ b/usm/managers/instructions.go @@ -11,9 +11,12 @@ func NewInstructionManager() gen.InstructionManager { []faststringmap.MapEntry[gen.InstructionDefinition]{ {Key: "", Value: usmisa.NewMove()}, - // Control Flow + // Functions {Key: "ret", Value: usmisa.NewRet()}, {Key: "call", Value: usmisa.NewCall()}, + + // Control Flow + {Key: "j", Value: usmisa.NewJump()}, {Key: "jz", Value: usmisa.NewJz()}, // Static Single Assignment (SSA) From b93664e001eea963ce4c6c4308eec0251a38c0b9 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Mon, 2 Jun 2025 16:33:15 +0300 Subject: [PATCH 30/33] Refactored SSA code, Added strict validation to `ret` --- aarch64/codegen/instruction.go | 2 +- aarch64/isa/add_test.go | 2 +- gen/function_generator.go | 4 +-- gen/instruction_info.go | 10 +++---- gen/translate.go | 23 +++++++++++++--- opt/dead_code_elimination.go | 4 +-- opt/ssa/construction_scheme.go | 7 ++--- opt/ssa/function_ssa_info.go | 32 ++++++++++++++++++++--- usm/isa/ret.go | 48 +++++++++++++++++++++++++++++++--- 9 files changed, 108 insertions(+), 24 deletions(-) diff --git a/aarch64/codegen/instruction.go b/aarch64/codegen/instruction.go index e364c69..14b8d27 100644 --- a/aarch64/codegen/instruction.go +++ b/aarch64/codegen/instruction.go @@ -34,7 +34,7 @@ func (ctx *InstructionCodegenContext) InstructionOffsetInFile() uint64 { func (ctx *InstructionCodegenContext) Codegen( buffer *bytes.Buffer, ) core.ResultList { - instruction, ok := ctx.InstructionInfo.Instruction.(Instruction) + instruction, ok := ctx.InstructionInfo.Definition.(Instruction) if !ok { return list.FromSingle(core.Result{ { diff --git a/aarch64/isa/add_test.go b/aarch64/isa/add_test.go index 12d37f2..38d3a2a 100644 --- a/aarch64/isa/add_test.go +++ b/aarch64/isa/add_test.go @@ -43,7 +43,7 @@ func buildInstructionFromSource( assert.True(t, results.IsEmpty()) assert.NotNil(t, info) - inst, ok := info.Instruction.(aarch64codegen.Instruction) + inst, ok := info.Definition.(aarch64codegen.Instruction) assert.True(t, ok) return info, inst diff --git a/gen/function_generator.go b/gen/function_generator.go index 3311865..24b6c19 100644 --- a/gen/function_generator.go +++ b/gen/function_generator.go @@ -144,7 +144,7 @@ func (g *FunctionGenerator) getInstructionBranchingDestinations( info *InstructionInfo, labels functionLabelData, ) ([]int, core.ResultList) { - steps, results := info.Instruction.PossibleNextSteps(info) + steps, results := info.Definition.PossibleNextSteps(info) if !results.IsEmpty() { return nil, results } @@ -286,7 +286,7 @@ func (g *FunctionGenerator) generateBasicBlocks( basicBlockLength := len(currentBasicBlock.Instructions) lastInstruction := currentBasicBlock.Instructions[basicBlockLength-1] - steps, results := lastInstruction.Instruction.PossibleNextSteps(lastInstruction) + steps, results := lastInstruction.Definition.PossibleNextSteps(lastInstruction) if !results.IsEmpty() { return results } diff --git a/gen/instruction_info.go b/gen/instruction_info.go index 37f91bb..1575eb9 100644 --- a/gen/instruction_info.go +++ b/gen/instruction_info.go @@ -13,7 +13,7 @@ type InstructionInfo struct { Arguments []ArgumentInfo // The actual instruction information, which is ISA specific. - Instruction InstructionDefinition + Definition InstructionDefinition // The location in which the instruction was defined in the source code. // Can be nil if the instruction was defined internally, for example, @@ -28,13 +28,13 @@ func NewEmptyInstructionInfo( BasicBlockInfo: nil, Targets: []*TargetInfo{}, Arguments: []ArgumentInfo{}, - Instruction: nil, + Definition: nil, Declaration: declaration, } } func (i *InstructionInfo) Validate() core.ResultList { - return i.Instruction.Validate(i) + return i.Definition.Validate(i) } // Appends the given register(s) as a target(s) of the instruction, @@ -66,12 +66,12 @@ func (i *InstructionInfo) AppendArgument(arguments ...ArgumentInfo) { // targets, for example, as an optimization to a more specific operation which // accepts the same arguments in certain cases. func (i *InstructionInfo) SetInstruction(instruction InstructionDefinition) { - i.Instruction = instruction + i.Definition = instruction } func (i *InstructionInfo) String() string { s := "" - operator := i.Instruction.Operator(i) + operator := i.Definition.Operator(i) if len(i.Targets) > 0 { for _, target := range i.Targets { diff --git a/gen/translate.go b/gen/translate.go index 75e9f53..f081732 100644 --- a/gen/translate.go +++ b/gen/translate.go @@ -51,6 +51,25 @@ func ArgumentToType(arg ArgumentInfo) (ReferencedTypeInfo, core.ResultList) { } } +func ArgumentsToTypes( + arguments []ArgumentInfo, +) ([]ReferencedTypeInfo, core.ResultList) { + results := core.ResultList{} + types := make([]ReferencedTypeInfo, len(arguments)) + + for i, arg := range arguments { + typ, curResults := ArgumentToType(arg) + results.Extend(&curResults) + types[i] = typ + } + + if !results.IsEmpty() { + return nil, results + } + + return types, core.ResultList{} +} + func ArgumentToLabel(arg ArgumentInfo) (*LabelInfo, core.ResultList) { if labelArg, ok := arg.(*LabelArgumentInfo); ok { return labelArg.Label, core.ResultList{} @@ -65,9 +84,7 @@ func ArgumentToLabel(arg ArgumentInfo) (*LabelInfo, core.ResultList) { }) } -func TargetsToRegisters( - targets []*TargetInfo, -) []*RegisterInfo { +func TargetsToRegisters(targets []*TargetInfo) []*RegisterInfo { registers := []*RegisterInfo{} for _, target := range targets { diff --git a/opt/dead_code_elimination.go b/opt/dead_code_elimination.go index 6c6dd54..2dcd8d9 100644 --- a/opt/dead_code_elimination.go +++ b/opt/dead_code_elimination.go @@ -89,7 +89,7 @@ func collectCriticalInstructions( for block := function.EntryBlock; block != nil; block = block.NextBlock { for _, instruction := range block.Instructions { - dceInstruction, ok := instruction.Instruction.(DCESupportedInstruction) + dceInstruction, ok := instruction.Definition.(DCESupportedInstruction) if !ok { curResults := newDCENotSupportedError(instruction) results.Extend(&curResults) @@ -117,7 +117,7 @@ func collectUsefulInstructions( unprocessedInstructions.Pop() processedInstructions.Add(instruction) - dceInstruction, ok := instruction.Instruction.(DCESupportedInstruction) + dceInstruction, ok := instruction.Definition.(DCESupportedInstruction) if !ok { // Should not happen, since we try to convert all instructions to // DCESupportedInstruction in the collection of critical instructions diff --git a/opt/ssa/construction_scheme.go b/opt/ssa/construction_scheme.go index 54bd182..3dbddfd 100644 --- a/opt/ssa/construction_scheme.go +++ b/opt/ssa/construction_scheme.go @@ -126,10 +126,11 @@ func (s *ReachingDefinitionsSet) popBlock() { s.registerDefinitionPushes.Pop() } -type PhiInstruction interface { - gen.BaseInstruction +type PhiInstructionDefinition interface { + gen.InstructionDefinition AddForwardingRegister( + *gen.InstructionInfo, *gen.BasicBlockInfo, *gen.RegisterInfo, ) core.ResultList @@ -146,7 +147,7 @@ type SsaConstructionScheme interface { NewPhiInstruction( *gen.BasicBlockInfo, *gen.RegisterInfo, - ) (PhiInstruction, core.ResultList) + ) (*gen.InstructionInfo, core.ResultList) // Creates a new unique register that is used as a renaming of the provided // register in the construction of the SSA form. diff --git a/opt/ssa/function_ssa_info.go b/opt/ssa/function_ssa_info.go index c612057..390d092 100644 --- a/opt/ssa/function_ssa_info.go +++ b/opt/ssa/function_ssa_info.go @@ -1,7 +1,10 @@ package ssa import ( + "fmt" + "alon.kr/x/graph" + "alon.kr/x/list" "alon.kr/x/set" "alon.kr/x/usm/core" "alon.kr/x/usm/gen" @@ -14,7 +17,7 @@ type forwardingRegisterDescriptor struct { type phiInstructionDescriptor struct { // The new phi instruction. - instruction PhiInstruction + *gen.InstructionInfo // The original base register that this phi instruction is a definition for. base *gen.RegisterInfo @@ -39,8 +42,29 @@ func (p *phiInstructionDescriptor) AddForwardingRegister( // will result in some weird behavior. func (p *phiInstructionDescriptor) CommitForwardingRegisters() core.ResultList { results := core.ResultList{} + phiDefinition, ok := p.Definition.(PhiInstructionDefinition) + + if !ok { + defName := p.Definition.Operator(p.InstructionInfo) + return list.FromSingle(core.Result{ + { + Type: core.InternalErrorResult, + Message: "Expected phi instruction definition", + Location: p.Declaration, + }, + { + Type: core.HintResult, + Message: fmt.Sprintf("Got \"%s\" instruction definition", defName), + }, + }) + } + for _, forward := range p.forwards { - curResults := p.instruction.AddForwardingRegister(forward.block, forward.renamed) + curResults := phiDefinition.AddForwardingRegister( + p.InstructionInfo, + forward.block, + forward.renamed, + ) results.Extend(&curResults) } @@ -185,8 +209,8 @@ func (i *FunctionSsaInfo) InsertPhiInstructions() core.ResultList { return results } descriptor := phiInstructionDescriptor{ - instruction: phi, - base: register, + InstructionInfo: phi, + base: register, } i.PhiInstructionsPerBlock[blockIndex] = append( i.PhiInstructionsPerBlock[blockIndex], diff --git a/usm/isa/ret.go b/usm/isa/ret.go index 2113cbe..7d217a8 100644 --- a/usm/isa/ret.go +++ b/usm/isa/ret.go @@ -1,6 +1,8 @@ package usmisa import ( + "fmt" + "alon.kr/x/usm/core" "alon.kr/x/usm/gen" "alon.kr/x/usm/opt" @@ -30,14 +32,54 @@ func (Ret) Validate(info *gen.InstructionInfo) core.ResultList { curResults := gen.AssertTargetsExactly(info, 0) results.Extend(&curResults) - // TODO: this is not exactly correct, arguments to the ret instruction must - // match the targets of the function. - curResults = gen.AssertArgumentsExactly(info, 0) + functionTargetTypes := info.FunctionInfo.Targets + retTypes, curResults := gen.ArgumentsToTypes(info.Arguments) results.Extend(&curResults) if !results.IsEmpty() { return results } + if len(functionTargetTypes) != len(retTypes) { + results.Append(core.Result{ + { + Type: core.ErrorResult, + Message: fmt.Sprintf( + "Number of arguments (%d) and the number of targets of the function (%d) do not match", + len(retTypes), + len(functionTargetTypes), + ), + Location: info.Declaration, + }, + }) + } + + if !results.IsEmpty() { + return results + } + + for i, funcTargetType := range functionTargetTypes { + retArgumentType := retTypes[i] + + if !funcTargetType.Equal(retArgumentType) { + results.Append(core.Result{ + { + Type: core.ErrorResult, + Message: "Return type does not match the type of the function target", + Location: retArgumentType.Declaration, + }, + { + Type: core.HintResult, + Message: "Matches this function target type", + Location: funcTargetType.Declaration, + }, + }) + } + } + + if !results.IsEmpty() { + return results + } + return core.ResultList{} } From bdf0085a9e56248d10e10bddac33e27bf922314d Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Mon, 2 Jun 2025 20:33:20 +0300 Subject: [PATCH 31/33] Added `usm` ssa transformation --- gen/instruction_generator.go | 2 +- gen/instruction_generator_test.go | 10 ++- gen/instruction_manager.go | 2 +- gen/instruction_map.go | 9 +- gen/register_argument.go | 4 +- usm.go | 7 ++ usm/isa/phi.go | 15 ++++ usm/ssa/ssa_construction.go | 142 ++++++++++++++++++++++++++++++ 8 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 usm/ssa/ssa_construction.go diff --git a/gen/instruction_generator.go b/gen/instruction_generator.go index 4eb1a81..89c721b 100644 --- a/gen/instruction_generator.go +++ b/gen/instruction_generator.go @@ -121,7 +121,7 @@ func (g *InstructionGenerator) Generate( } instName := ViewToSourceString(ctx.FileGenerationContext, node.Operator) - instDef, results := ctx.Instructions.GetInstructionDefinition(instName, node) + instDef, results := ctx.Instructions.GetInstructionDefinition(instName, &node) arguments, curResults := g.generateArguments(&instCtx, node) results.Extend(&curResults) diff --git a/gen/instruction_generator_test.go b/gen/instruction_generator_test.go index 637014e..04b1572 100644 --- a/gen/instruction_generator_test.go +++ b/gen/instruction_generator_test.go @@ -104,16 +104,22 @@ type InstructionMap map[string]gen.InstructionDefinition func (m *InstructionMap) GetInstructionDefinition( name string, - node parse.InstructionNode, + node *parse.InstructionNode, ) (gen.InstructionDefinition, core.ResultList) { inst, ok := (*m)[name] if !ok { + v := (*core.UnmanagedSourceView)(nil) + if node != nil { + v = &node.Operator + } + return nil, list.FromSingle(core.Result{{ Type: core.ErrorResult, Message: "undefined instruction", - Location: &node.Operator, + Location: v, }}) } + return inst, core.ResultList{} } diff --git a/gen/instruction_manager.go b/gen/instruction_manager.go index c4f5f6b..7935538 100644 --- a/gen/instruction_manager.go +++ b/gen/instruction_manager.go @@ -12,6 +12,6 @@ type InstructionManager interface { // generating nice error messages. GetInstructionDefinition( name string, - node parse.InstructionNode, + node *parse.InstructionNode, ) (InstructionDefinition, core.ResultList) } diff --git a/gen/instruction_map.go b/gen/instruction_map.go index 09195c1..a945442 100644 --- a/gen/instruction_map.go +++ b/gen/instruction_map.go @@ -16,7 +16,7 @@ type InstructionMap struct { func (m *InstructionMap) GetInstructionDefinition( name string, - node parse.InstructionNode, + node *parse.InstructionNode, ) (InstructionDefinition, core.ResultList) { if !m.caseSensitive { name = strings.ToLower(name) @@ -24,10 +24,15 @@ func (m *InstructionMap) GetInstructionDefinition( def, ok := m.Map.LookupString(name) if !ok { + v := (*core.UnmanagedSourceView)(nil) + if node != nil { + v = &node.Operator + } + return nil, list.FromSingle(core.Result{{ Type: core.ErrorResult, Message: "Undefined instruction", - Location: &node.Operator, + Location: v, }}) } diff --git a/gen/register_argument.go b/gen/register_argument.go index 91590dc..1891a55 100644 --- a/gen/register_argument.go +++ b/gen/register_argument.go @@ -13,8 +13,8 @@ type RegisterArgumentInfo struct { declaration *core.UnmanagedSourceView } -func NewRegisterArgument(register *RegisterInfo) RegisterArgumentInfo { - return RegisterArgumentInfo{ +func NewRegisterArgumentInfo(register *RegisterInfo) *RegisterArgumentInfo { + return &RegisterArgumentInfo{ Register: register, } } diff --git a/usm.go b/usm.go index b6c0024..3ebdc7a 100644 --- a/usm.go +++ b/usm.go @@ -13,6 +13,7 @@ import ( "alon.kr/x/usm/parse" "alon.kr/x/usm/transform" usmmanagers "alon.kr/x/usm/usm/managers" + usmssa "alon.kr/x/usm/usm/ssa" "github.com/spf13/cobra" ) @@ -29,6 +30,12 @@ var targets = transform.NewTargetCollection( TargetName: "usm", // Transform: , }, + &transform.Transformation{ + Names: []string{"static-single-assignment", "ssa"}, + Description: "An optimization pass that converts the assembly to static single assignment form", + TargetName: "usm", + Transform: usmssa.TransformFileToSsaForm, + }, &transform.Transformation{ Names: []string{"aarch64", "arm64"}, Description: "Converts the universal assembly to matching machine specific AArch64 assembly", diff --git a/usm/isa/phi.go b/usm/isa/phi.go index 8d0219f..7434e53 100644 --- a/usm/isa/phi.go +++ b/usm/isa/phi.go @@ -98,5 +98,20 @@ func (i Phi) Validate(info *gen.InstructionInfo) core.ResultList { } } + // Notice that we do not check if incomingEdges is empty. + // This is because it it VALID for some incoming edges to not have a + // specified value. In that case the value will be undefined. + return results } + +func (Phi) AddForwardingRegister( + instruction *gen.InstructionInfo, + block *gen.BasicBlockInfo, + register *gen.RegisterInfo, +) core.ResultList { + labelArg := gen.NewLabelArgumentInfo(block.Label) + regArg := gen.NewRegisterArgumentInfo(register) + instruction.AppendArgument(labelArg, regArg) + return core.ResultList{} +} diff --git a/usm/ssa/ssa_construction.go b/usm/ssa/ssa_construction.go new file mode 100644 index 0000000..40b2eaa --- /dev/null +++ b/usm/ssa/ssa_construction.go @@ -0,0 +1,142 @@ +package usmssa + +import ( + "fmt" + + "alon.kr/x/usm/core" + "alon.kr/x/usm/gen" + "alon.kr/x/usm/opt/ssa" + "alon.kr/x/usm/transform" + usmisa "alon.kr/x/usm/usm/isa" +) + +type ConstructionScheme struct { + RenamesPerRegister map[*gen.RegisterInfo]uint +} + +func NewConstructionScheme() ssa.SsaConstructionScheme { + return &ConstructionScheme{ + RenamesPerRegister: make(map[*gen.RegisterInfo]uint), + } +} + +func (s *ConstructionScheme) NewPhiInstruction( + block *gen.BasicBlockInfo, + register *gen.RegisterInfo, +) (*gen.InstructionInfo, core.ResultList) { + info := gen.NewEmptyInstructionInfo(nil) + info.SetInstruction(usmisa.NewPhi()) + + target := gen.NewTargetInfo(register) + info.AppendTarget(&target) + + block.PrependInstruction(info) + + return info, core.ResultList{} +} + +func (s *ConstructionScheme) NewRenamedRegister( + register *gen.RegisterInfo, +) *gen.RegisterInfo { + renamedNumber := s.RenamesPerRegister[register] + s.RenamesPerRegister[register]++ + renamedName := register.Name + "_" + fmt.Sprint(renamedNumber) + return gen.NewRegisterInfo(renamedName, register.Type) +} + +func (s *ConstructionScheme) renameArgument( + argument gen.ArgumentInfo, + reachingSet ssa.ReachingDefinitionsSet, +) core.ResultList { + if argument, ok := argument.(*gen.RegisterArgumentInfo); ok { + baseRegister := argument.Register + renamedRegister := reachingSet.GetReachingDefinition(baseRegister) + argument.SwitchRegister(renamedRegister) + } + + return core.ResultList{} +} + +func (s *ConstructionScheme) renameTarget( + instruction *gen.InstructionInfo, + target *gen.TargetInfo, + reachingSet ssa.ReachingDefinitionsSet, +) core.ResultList { + baseRegister := target.Register + renamedRegister := reachingSet.RenameDefinitionRegister(baseRegister) + instruction.SwitchTarget(target, renamedRegister) + return core.ResultList{} +} + +func (s *ConstructionScheme) renameInstruction( + instruction *gen.InstructionInfo, + reachingSet ssa.ReachingDefinitionsSet, +) core.ResultList { + // First, we rename the arguments. + for _, argument := range instruction.Arguments { + results := s.renameArgument(argument, reachingSet) + if !results.IsEmpty() { + return results + } + } + + // Then, we define the new registers (targets). + for _, target := range instruction.Targets { + results := s.renameTarget(instruction, target, reachingSet) + if !results.IsEmpty() { + return results + } + } + + return core.ResultList{} +} + +func (s *ConstructionScheme) RenameBasicBlock( + block *gen.BasicBlockInfo, + reachingSet ssa.ReachingDefinitionsSet, +) core.ResultList { + for _, instruction := range block.Instructions { + results := s.renameInstruction(instruction, reachingSet) + if !results.IsEmpty() { + return results + } + } + + return core.ResultList{} +} + +func FunctionToSsaForm(function *gen.FunctionInfo) core.ResultList { + constructionScheme := NewConstructionScheme() + ssaInfo := ssa.NewFunctionSsaInfo(function, constructionScheme) + results := ssaInfo.InsertPhiInstructions() + if !results.IsEmpty() { + return results + } + + results = ssaInfo.RenameRegisters() + if !results.IsEmpty() { + return results + } + + return core.ResultList{} +} + +func FileToSsaForm(file *gen.FileInfo) core.ResultList { + results := core.ResultList{} + + for _, function := range file.Functions { + if function.IsDefined() { + curResults := FunctionToSsaForm(function) + results.Extend(&curResults) + } + } + + return results +} + +func TransformFileToSsaForm( + data *transform.TargetData, +) (*transform.TargetData, core.ResultList) { + results := FileToSsaForm(data.Code) + return data, results +} From 7c3ea645c720fdac4f865e4327d69ef6fbae1c58 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Mon, 2 Jun 2025 20:49:07 +0300 Subject: [PATCH 32/33] Removed old `usm64` isa --- usm64/core/emualtor.go | 49 ---------- usm64/core/emulation.go | 99 -------------------- usm64/core/instruction.go | 8 -- usm64/core/label.go | 27 ------ usm64/core/register.go | 32 ------- usm64/isa/add.go | 60 ------------ usm64/isa/base_instruction.go | 37 -------- usm64/isa/critical_instruction.go | 16 ---- usm64/isa/fixed.go | 94 ------------------- usm64/isa/jump.go | 42 --------- usm64/isa/jump_not_zero.go | 57 ------------ usm64/isa/jump_zero.go | 57 ------------ usm64/isa/move.go | 46 ---------- usm64/isa/non_branching_instruction.go | 19 ---- usm64/isa/phi.go | 59 ------------ usm64/isa/put.go | 46 ---------- usm64/isa/terminate.go | 46 ---------- usm64/managers/context.go | 24 ----- usm64/managers/instructions.go | 33 ------- usm64/managers/registers.go | 42 --------- usm64/managers/types.go | 35 ------- usm64/ssa/ssa_construction.go | 121 ------------------------- 22 files changed, 1049 deletions(-) delete mode 100644 usm64/core/emualtor.go delete mode 100644 usm64/core/emulation.go delete mode 100644 usm64/core/instruction.go delete mode 100644 usm64/core/label.go delete mode 100644 usm64/core/register.go delete mode 100644 usm64/isa/add.go delete mode 100644 usm64/isa/base_instruction.go delete mode 100644 usm64/isa/critical_instruction.go delete mode 100644 usm64/isa/fixed.go delete mode 100644 usm64/isa/jump.go delete mode 100644 usm64/isa/jump_not_zero.go delete mode 100644 usm64/isa/jump_zero.go delete mode 100644 usm64/isa/move.go delete mode 100644 usm64/isa/non_branching_instruction.go delete mode 100644 usm64/isa/phi.go delete mode 100644 usm64/isa/put.go delete mode 100644 usm64/isa/terminate.go delete mode 100644 usm64/managers/context.go delete mode 100644 usm64/managers/instructions.go delete mode 100644 usm64/managers/registers.go delete mode 100644 usm64/managers/types.go delete mode 100644 usm64/ssa/ssa_construction.go diff --git a/usm64/core/emualtor.go b/usm64/core/emualtor.go deleted file mode 100644 index 3965f39..0000000 --- a/usm64/core/emualtor.go +++ /dev/null @@ -1,49 +0,0 @@ -package usm64core - -import ( - "alon.kr/x/list" - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" -) - -func instructionInfoToInstruction(info *gen.InstructionInfo) (Instruction, core.ResultList) { - if inst, ok := info.Instruction.(Instruction); !ok { - return nil, list.FromSingle(core.Result{{ - Type: core.InternalErrorResult, - Message: "Invalid instruction type", - Location: info.Declaration, - }}) - } else { - return inst, core.ResultList{} - } -} - -type Emulator struct{} - -func (Emulator) Emulate( - function *gen.FunctionInfo, -) core.ResultList { - ctx, results := NewEmulationContext(function) - if !results.IsEmpty() { - return results - } - - for !ctx.ShouldTerminate { - instInfo := ctx.NextBlockInfo.Instructions[ctx.NextInstructionIndexInBlock] - instruction, results := instructionInfoToInstruction(instInfo) - if !results.IsEmpty() { - return results - } - - results = instruction.Emulate(ctx) - if !results.IsEmpty() { - return results - } - } - - return core.ResultList{} -} - -func NewEmulator() Emulator { - return Emulator{} -} diff --git a/usm64/core/emulation.go b/usm64/core/emulation.go deleted file mode 100644 index 7b0fe99..0000000 --- a/usm64/core/emulation.go +++ /dev/null @@ -1,99 +0,0 @@ -package usm64core - -import ( - "alon.kr/x/list" - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" -) - -// MARK: Error - -type EmulationError interface{} - -// MARK: Context - -type EmulationContext struct { - NextBlockInfo *gen.BasicBlockInfo - NextInstructionIndexInBlock uint - ShouldTerminate bool - - Registers map[string]uint64 -} - -func (ctx *EmulationContext) JumpToLabel(label *gen.LabelInfo) core.ResultList { - ctx.NextBlockInfo = label.BasicBlock - ctx.NextInstructionIndexInBlock = 0 - return core.ResultList{} -} - -func (ctx *EmulationContext) ContinueToNextInstruction() core.ResultList { - ctx.NextInstructionIndexInBlock++ - if uint(len(ctx.NextBlockInfo.Instructions)) == ctx.NextInstructionIndexInBlock { - ctx.NextBlockInfo = ctx.NextBlockInfo.NextBlock - ctx.NextInstructionIndexInBlock = 0 - } - return core.ResultList{} -} - -func (ctx *EmulationContext) ArgumentToValue( - argument gen.ArgumentInfo, -) (uint64, core.ResultList) { - switch typedArgument := argument.(type) { - case *gen.RegisterArgumentInfo: - name := typedArgument.Register.Name - value, ok := ctx.Registers[name] - if !ok { - v := argument.Declaration() - return 0, list.FromSingle(core.Result{{ - Type: core.InternalErrorResult, - Message: "Undefined register", - Location: v, - }}) - } - - return value, core.ResultList{} - - case *gen.ImmediateInfo: - if !typedArgument.Value.IsInt64() { - v := argument.Declaration() - return 0, list.FromSingle(core.Result{{ - Type: core.ErrorResult, - Message: "Immediate overflows 64 bits", - Location: v, - }}) - } - - return uint64(typedArgument.Value.Int64()), core.ResultList{} - - case *gen.LabelArgumentInfo: - v := argument.Declaration() - return 0, list.FromSingle(core.Result{{ - Type: core.ErrorResult, - Message: "Expected valued argument", - Location: v, - }}) - - default: - v := argument.Declaration() - return 0, list.FromSingle(core.Result{{ - Type: core.InternalErrorResult, - Message: "Unexpected argument type", - Location: v, - }}) - } -} - -func NewEmulationContext( - function *gen.FunctionInfo, -) (*EmulationContext, core.ResultList) { - return &EmulationContext{ - NextInstructionIndexInBlock: 0, - NextBlockInfo: function.EntryBlock, - ShouldTerminate: false, - Registers: make(map[string]uint64), - }, core.ResultList{} -} - -type Emulateable interface { - Emulate(ctx *EmulationContext) core.ResultList -} diff --git a/usm64/core/instruction.go b/usm64/core/instruction.go deleted file mode 100644 index 2ca2a74..0000000 --- a/usm64/core/instruction.go +++ /dev/null @@ -1,8 +0,0 @@ -package usm64core - -// MARK: Instruction - -type Instruction interface { - // Emulate (interpret) the instruction, provided the context. - Emulateable -} diff --git a/usm64/core/label.go b/usm64/core/label.go deleted file mode 100644 index 80e25f7..0000000 --- a/usm64/core/label.go +++ /dev/null @@ -1,27 +0,0 @@ -package usm64core - -import ( - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" -) - -type Label struct { - Name string - InstructionIndex uint64 - declaration *core.UnmanagedSourceView -} - -func NewLabel(arg gen.LabelArgumentInfo) (Label, core.ResultList) { - return Label{ - Name: arg.Label.Name, - declaration: arg.Declaration(), - }, core.ResultList{} -} - -func (l Label) String(*EmulationContext) string { - return l.Name -} - -func (l Label) Declaration() *core.UnmanagedSourceView { - return l.declaration -} diff --git a/usm64/core/register.go b/usm64/core/register.go deleted file mode 100644 index 6bb4a93..0000000 --- a/usm64/core/register.go +++ /dev/null @@ -1,32 +0,0 @@ -package usm64core - -import ( - "fmt" - - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" -) - -type Register struct { - Name string - declaration *core.UnmanagedSourceView -} - -func NewRegister(arg *gen.RegisterArgumentInfo) (Register, core.ResultList) { - return Register{ - Name: arg.Register.Name, - declaration: arg.Declaration(), - }, core.ResultList{} -} - -func (r Register) Value(ctx *EmulationContext) uint64 { - return ctx.Registers[r.Name] -} - -func (r Register) String(ctx *EmulationContext) string { - return fmt.Sprintf("%s (#%d)", r.Name, r.Value(ctx)) -} - -func (r Register) Declaration() *core.UnmanagedSourceView { - return r.declaration -} diff --git a/usm64/isa/add.go b/usm64/isa/add.go deleted file mode 100644 index 40288c3..0000000 --- a/usm64/isa/add.go +++ /dev/null @@ -1,60 +0,0 @@ -package usm64isa - -import ( - "math/bits" - - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" - usm64core "alon.kr/x/usm/usm64/core" -) - -type AddInstruction struct { - nonBranchingInstruction -} - -func (i *AddInstruction) Emulate( - ctx *usm64core.EmulationContext, -) core.ResultList { - results := core.ResultList{} - - first, firstResults := ctx.ArgumentToValue(i.Arguments[0]) - results.Extend(&firstResults) - - second, secondResults := ctx.ArgumentToValue(i.Arguments[1]) - results.Extend(&secondResults) - - if !results.IsEmpty() { - return results - } - - targetName := i.Targets[0].Register.Name - sum, _ := bits.Add64(first, second, 0) - ctx.Registers[targetName] = sum - return ctx.ContinueToNextInstruction() -} - -func (i *AddInstruction) Operator() string { - return "ADD" -} - -func NewAddInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - results := core.ResultList{} - - if !results.IsEmpty() { - return nil, results - } - - return gen.BaseInstruction(&AddInstruction{ - nonBranchingInstruction: newNonBranchingInstruction(info), - }), core.ResultList{} -} - -func NewAddInstructionDefinition() gen.InstructionDefinition { - return &FixedInstructionDefinition{ - Targets: 1, - Arguments: 2, - Creator: NewAddInstruction, - } -} diff --git a/usm64/isa/base_instruction.go b/usm64/isa/base_instruction.go deleted file mode 100644 index 95a2fb5..0000000 --- a/usm64/isa/base_instruction.go +++ /dev/null @@ -1,37 +0,0 @@ -package usm64isa - -import ( - "alon.kr/x/usm/gen" -) - -type baseInstruction struct { - // A pointer the the internal USM representation of the instruction. - // This in turn has the representation of the arguments, targets, and types. - *gen.InstructionInfo -} - -func newBaseInstruction(info *gen.InstructionInfo) baseInstruction { - return baseInstruction{info} -} - -func (i *baseInstruction) Uses() []*gen.RegisterInfo { - arguments := i.InstructionInfo.Arguments - registers := []*gen.RegisterInfo{} - for _, argument := range arguments { - if argument, ok := argument.(*gen.RegisterArgumentInfo); ok { - registers = append(registers, argument.Register) - } - } - - return registers -} - -func (i *baseInstruction) Defines() []*gen.RegisterInfo { - targets := i.InstructionInfo.Targets - registers := []*gen.RegisterInfo{} - for _, target := range targets { - registers = append(registers, target.Register) - } - - return registers -} diff --git a/usm64/isa/critical_instruction.go b/usm64/isa/critical_instruction.go deleted file mode 100644 index 2886d77..0000000 --- a/usm64/isa/critical_instruction.go +++ /dev/null @@ -1,16 +0,0 @@ -package usm64isa - -// A critical instruction is an instruction that cannot be removed by the -// dead code elimination process. This means that it has side effects, or -// is a function call, or is a branch, etc. -type CriticalInstruction struct{} - -func (i *CriticalInstruction) IsCritical() bool { - return true -} - -type NotCriticalInstruction struct{} - -func (i *NotCriticalInstruction) IsCritical() bool { - return false -} diff --git a/usm64/isa/fixed.go b/usm64/isa/fixed.go deleted file mode 100644 index 1f21416..0000000 --- a/usm64/isa/fixed.go +++ /dev/null @@ -1,94 +0,0 @@ -package usm64isa - -import ( - "fmt" - - "alon.kr/x/list" - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" -) - -type FixedInstructionDefinition struct { - Targets core.UsmUint - Arguments core.UsmUint - Creator func( - info *gen.InstructionInfo, - ) (gen.BaseInstruction, core.ResultList) -} - -func (d *FixedInstructionDefinition) InferTargetTypes( - ctx *gen.FunctionGenerationContext, - targets []*gen.ReferencedTypeInfo, - arguments []*gen.ReferencedTypeInfo, -) ([]gen.ReferencedTypeInfo, core.ResultList) { - base := ctx.Types.GetType("$64") - if base == nil { - return nil, list.FromSingle(core.Result{ - { - Type: core.InternalErrorResult, - Message: "The $64 type is not defined", - }, - }) - } - - inferredTargets := make([]gen.ReferencedTypeInfo, len(targets)) - for i := core.UsmUint(0); i < d.Targets; i++ { - inferredTargets[i] = gen.ReferencedTypeInfo{Base: base} - } - - return inferredTargets, core.ResultList{} -} - -func (d *FixedInstructionDefinition) assertTargetAmount( - targets []*gen.TargetInfo, -) core.ResultList { - // TODO: possible overflow? - if core.UsmUint(len(targets)) != d.Targets { - return list.FromSingle(core.Result{ - { - Type: core.ErrorResult, - Message: fmt.Sprintf("Exactly %d target(s) are allowed", d.Targets), - }, - }) - } - return core.ResultList{} -} - -func (d *FixedInstructionDefinition) assertArgumentAmount( - arguments []gen.ArgumentInfo, -) core.ResultList { - // TODO: possible overflow? - if core.UsmUint(len(arguments)) != d.Arguments { - return list.FromSingle(core.Result{ - { - Type: core.ErrorResult, - Message: fmt.Sprintf("Exactly %d argument(s) are allowed", d.Arguments), - }, - }) - } - return core.ResultList{} -} - -func (d *FixedInstructionDefinition) assertInputLengths( - targetInfos []*gen.TargetInfo, - argumentInfos []gen.ArgumentInfo, -) (results core.ResultList) { - targetResults := d.assertTargetAmount(targetInfos) - results.Extend(&targetResults) - - argumentResults := d.assertArgumentAmount(argumentInfos) - results.Extend(&argumentResults) - - return results -} - -func (d *FixedInstructionDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - results := d.assertInputLengths(info.Targets, info.Arguments) - if !results.IsEmpty() { - return nil, results - } - - return d.Creator(info) -} diff --git a/usm64/isa/jump.go b/usm64/isa/jump.go deleted file mode 100644 index 940ffc5..0000000 --- a/usm64/isa/jump.go +++ /dev/null @@ -1,42 +0,0 @@ -package usm64isa - -import ( - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" - usm64core "alon.kr/x/usm/usm64/core" -) - -type JumpInstruction struct { - baseInstruction - CriticalInstruction -} - -func (i *JumpInstruction) PossibleNextSteps() (gen.StepInfo, core.ResultList) { - label := i.InstructionInfo.Arguments[0].(*gen.LabelArgumentInfo).Label - return gen.StepInfo{PossibleBranches: []*gen.LabelInfo{label}}, core.ResultList{} -} - -func (i *JumpInstruction) Emulate( - ctx *usm64core.EmulationContext, -) core.ResultList { - labelArgument := i.InstructionInfo.Arguments[0].(*gen.LabelArgumentInfo) - return ctx.JumpToLabel(labelArgument.Label) -} - -func (i *JumpInstruction) Operator() string { - return "J" -} - -func NewJumpInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - return gen.BaseInstruction(&JumpInstruction{baseInstruction: baseInstruction{info}}), core.ResultList{} -} - -func NewJumpInstructionDefinition() gen.InstructionDefinition { - return &FixedInstructionDefinition{ - Targets: 0, - Arguments: 1, - Creator: NewJumpInstruction, - } -} diff --git a/usm64/isa/jump_not_zero.go b/usm64/isa/jump_not_zero.go deleted file mode 100644 index b8277c1..0000000 --- a/usm64/isa/jump_not_zero.go +++ /dev/null @@ -1,57 +0,0 @@ -package usm64isa - -import ( - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" - usm64core "alon.kr/x/usm/usm64/core" -) - -type JumpNotZeroInstruction struct { - baseInstruction - CriticalInstruction -} - -func (i *JumpNotZeroInstruction) PossibleNextSteps() (gen.StepInfo, core.ResultList) { - label := i.InstructionInfo.Arguments[1].(*gen.LabelArgumentInfo).Label - return gen.StepInfo{ - PossibleBranches: []*gen.LabelInfo{label}, - PossibleContinue: true, - }, core.ResultList{} -} - -func (i *JumpNotZeroInstruction) Emulate( - ctx *usm64core.EmulationContext, -) core.ResultList { - value, results := ctx.ArgumentToValue(i.InstructionInfo.Arguments[0]) - if !results.IsEmpty() { - return results - } - - if value != uint64(0) { - labelArgument := i.InstructionInfo.Arguments[1].(*gen.LabelArgumentInfo) - return ctx.JumpToLabel(labelArgument.Label) - - } else { - return ctx.ContinueToNextInstruction() - } -} - -func (i *JumpNotZeroInstruction) Operator() string { - return "JNZ" -} - -func NewJumpNotZeroInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - return gen.BaseInstruction(&JumpNotZeroInstruction{ - baseInstruction: newBaseInstruction(info), - }), core.ResultList{} -} - -func NewJumpNotZeroInstructionDefinition() gen.InstructionDefinition { - return &FixedInstructionDefinition{ - Targets: 0, - Arguments: 2, - Creator: NewJumpNotZeroInstruction, - } -} diff --git a/usm64/isa/jump_zero.go b/usm64/isa/jump_zero.go deleted file mode 100644 index 6d4f05b..0000000 --- a/usm64/isa/jump_zero.go +++ /dev/null @@ -1,57 +0,0 @@ -package usm64isa - -import ( - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" - usm64core "alon.kr/x/usm/usm64/core" -) - -type JumpZeroInstruction struct { - baseInstruction - CriticalInstruction -} - -func (i *JumpZeroInstruction) PossibleNextSteps() (gen.StepInfo, core.ResultList) { - label := i.InstructionInfo.Arguments[1].(*gen.LabelArgumentInfo).Label - return gen.StepInfo{ - PossibleBranches: []*gen.LabelInfo{label}, - PossibleContinue: true, - }, core.ResultList{} -} - -func (i *JumpZeroInstruction) Emulate( - ctx *usm64core.EmulationContext, -) core.ResultList { - value, results := ctx.ArgumentToValue(i.InstructionInfo.Arguments[0]) - if !results.IsEmpty() { - return results - } - - if value == uint64(0) { - labelArgument := i.InstructionInfo.Arguments[1].(*gen.LabelArgumentInfo) - return ctx.JumpToLabel(labelArgument.Label) - - } else { - return ctx.ContinueToNextInstruction() - } -} - -func (i *JumpZeroInstruction) Operator() string { - return "JZ" -} - -func NewJumpZeroInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - return gen.BaseInstruction(&JumpZeroInstruction{ - baseInstruction: newBaseInstruction(info), - }), core.ResultList{} -} - -func NewJumpZeroInstructionDefinition() gen.InstructionDefinition { - return &FixedInstructionDefinition{ - Targets: 0, - Arguments: 2, - Creator: NewJumpZeroInstruction, - } -} diff --git a/usm64/isa/move.go b/usm64/isa/move.go deleted file mode 100644 index 6d5b04b..0000000 --- a/usm64/isa/move.go +++ /dev/null @@ -1,46 +0,0 @@ -package usm64isa - -import ( - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" - usm64core "alon.kr/x/usm/usm64/core" -) - -// Currently, supporting only a single argument + target. -// TODO: support n arguments and target pairs. -type MoveInstruction struct { - nonBranchingInstruction -} - -func (i *MoveInstruction) Emulate( - ctx *usm64core.EmulationContext, -) core.ResultList { - value, results := ctx.ArgumentToValue(i.Arguments[0]) - if !results.IsEmpty() { - return results - } - - targetName := i.Targets[0].Register.Name - ctx.Registers[targetName] = value - return ctx.ContinueToNextInstruction() -} - -func (i *MoveInstruction) Operator() string { - return "" -} - -func NewMoveInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - return gen.BaseInstruction(&MoveInstruction{ - nonBranchingInstruction: newNonBranchingInstruction(info), - }), core.ResultList{} -} - -func NewMoveInstructionDefinition() gen.InstructionDefinition { - return &FixedInstructionDefinition{ - Targets: 1, - Arguments: 1, - Creator: NewMoveInstruction, - } -} diff --git a/usm64/isa/non_branching_instruction.go b/usm64/isa/non_branching_instruction.go deleted file mode 100644 index 2af7019..0000000 --- a/usm64/isa/non_branching_instruction.go +++ /dev/null @@ -1,19 +0,0 @@ -package usm64isa - -import ( - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" -) - -type nonBranchingInstruction struct { - baseInstruction - NotCriticalInstruction -} - -func newNonBranchingInstruction(info *gen.InstructionInfo) nonBranchingInstruction { - return nonBranchingInstruction{baseInstruction: newBaseInstruction(info)} -} - -func (i *nonBranchingInstruction) PossibleNextSteps() (gen.StepInfo, core.ResultList) { - return gen.StepInfo{PossibleContinue: true}, core.ResultList{} -} diff --git a/usm64/isa/phi.go b/usm64/isa/phi.go deleted file mode 100644 index 5b14bde..0000000 --- a/usm64/isa/phi.go +++ /dev/null @@ -1,59 +0,0 @@ -package usm64isa - -import ( - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" - usm64core "alon.kr/x/usm/usm64/core" -) - -type PhiInstruction struct { - nonBranchingInstruction -} - -func (i *PhiInstruction) Emulate( - ctx *usm64core.EmulationContext, -) core.ResultList { - labelArgument := i.InstructionInfo.Arguments[0].(*gen.LabelArgumentInfo) - return ctx.JumpToLabel(labelArgument.Label) -} - -func NewPhiInstruction( - info *gen.InstructionInfo, -) (*PhiInstruction, core.ResultList) { - return &PhiInstruction{newNonBranchingInstruction(info)}, core.ResultList{} -} - -func (i *PhiInstruction) Operator() string { - return "PHI" -} - -func (i *PhiInstruction) AddForwardingRegister( - block *gen.BasicBlockInfo, - register *gen.RegisterInfo, -) core.ResultList { - labelArgument := gen.NewLabelArgumentInfo(block.Label) - registerArgument := gen.NewRegisterArgument(register) - i.AppendArgument(labelArgument, ®isterArgument) - return core.ResultList{} -} - -type PhiInstructionDefinition struct{} - -func (d *PhiInstructionDefinition) BuildInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - // TODO: argument validation - return NewPhiInstruction(info) -} - -func (d *PhiInstructionDefinition) InferTargetTypes( - ctx *gen.FunctionGenerationContext, - targets []*gen.ReferencedTypeInfo, - arguments []*gen.ReferencedTypeInfo, -) ([]gen.ReferencedTypeInfo, core.ResultList) { - return []gen.ReferencedTypeInfo{}, core.ResultList{} -} - -func NewPhiInstructionDefinition() gen.InstructionDefinition { - return &PhiInstructionDefinition{} -} diff --git a/usm64/isa/put.go b/usm64/isa/put.go deleted file mode 100644 index 1d5de53..0000000 --- a/usm64/isa/put.go +++ /dev/null @@ -1,46 +0,0 @@ -package usm64isa - -import ( - "fmt" - - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" - usm64core "alon.kr/x/usm/usm64/core" -) - -type PutInstruction struct { - nonBranchingInstruction - CriticalInstruction -} - -func (i *PutInstruction) Emulate( - ctx *usm64core.EmulationContext, -) core.ResultList { - value, results := ctx.ArgumentToValue(i.Arguments[0]) - if !results.IsEmpty() { - return results - } - - fmt.Println(value) - return ctx.ContinueToNextInstruction() -} - -func (i *PutInstruction) Operator() string { - return "PUT" -} - -func NewPutInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - return gen.BaseInstruction(&PutInstruction{ - nonBranchingInstruction: newNonBranchingInstruction(info), - }), core.ResultList{} -} - -func NewPutInstructionDefinition() gen.InstructionDefinition { - return &FixedInstructionDefinition{ - Targets: 0, - Arguments: 1, - Creator: NewPutInstruction, - } -} diff --git a/usm64/isa/terminate.go b/usm64/isa/terminate.go deleted file mode 100644 index 4c66448..0000000 --- a/usm64/isa/terminate.go +++ /dev/null @@ -1,46 +0,0 @@ -package usm64isa - -import ( - "fmt" - - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" - usm64core "alon.kr/x/usm/usm64/core" -) - -type TerminateInstruction struct { - baseInstruction - CriticalInstruction -} - -func (i *TerminateInstruction) PossibleNextSteps() (gen.StepInfo, core.ResultList) { - return gen.StepInfo{PossibleReturn: true}, core.ResultList{} -} - -func (i *TerminateInstruction) Operator() string { - return "TERM" -} - -func (i *TerminateInstruction) Emulate( - ctx *usm64core.EmulationContext, -) core.ResultList { - fmt.Println("[Terminate]") - ctx.ShouldTerminate = true - return core.ResultList{} -} - -func NewTerminateInstruction( - info *gen.InstructionInfo, -) (gen.BaseInstruction, core.ResultList) { - return gen.BaseInstruction(&TerminateInstruction{ - baseInstruction: newBaseInstruction(info), - }), core.ResultList{} -} - -func NewTerminateInstructionDefinition() gen.InstructionDefinition { - return &FixedInstructionDefinition{ - Targets: 0, - Arguments: 0, - Creator: NewTerminateInstruction, - } -} diff --git a/usm64/managers/context.go b/usm64/managers/context.go deleted file mode 100644 index e19fc58..0000000 --- a/usm64/managers/context.go +++ /dev/null @@ -1,24 +0,0 @@ -package usm64managers - -import ( - "math/big" - - "alon.kr/x/usm/gen" -) - -func NewManagerCreators() gen.ManagerCreators { - return gen.ManagerCreators{ - RegisterManagerCreator: NewRegisterManager, - LabelManagerCreator: gen.NewLabelMap, - TypeManagerCreator: NewTypeManager, - GlobalManagerCreator: gen.NewGlobalMap, - } -} - -func NewGenerationContext() *gen.GenerationContext { - return &gen.GenerationContext{ - ManagerCreators: NewManagerCreators(), - Instructions: NewInstructionManager(), - PointerSize: big.NewInt(64), - } -} diff --git a/usm64/managers/instructions.go b/usm64/managers/instructions.go deleted file mode 100644 index 4267ab1..0000000 --- a/usm64/managers/instructions.go +++ /dev/null @@ -1,33 +0,0 @@ -package usm64managers - -import ( - "alon.kr/x/faststringmap" - "alon.kr/x/usm/gen" - usm64isa "alon.kr/x/usm/usm64/isa" -) - -func NewInstructionManager() gen.InstructionManager { - return gen.NewInstructionMap( - []faststringmap.MapEntry[gen.InstructionDefinition]{ - // mov - {Key: "", Value: usm64isa.NewMoveInstructionDefinition()}, - {Key: "mov", Value: usm64isa.NewMoveInstructionDefinition()}, - - // arithmetic - {Key: "add", Value: usm64isa.NewAddInstructionDefinition()}, - - // control flow - {Key: "j", Value: usm64isa.NewJumpInstructionDefinition()}, - {Key: "jz", Value: usm64isa.NewJumpZeroInstructionDefinition()}, - {Key: "jnz", Value: usm64isa.NewJumpNotZeroInstructionDefinition()}, - - // SSA - {Key: "phi", Value: usm64isa.NewPhiInstructionDefinition()}, - - // debug - {Key: "put", Value: usm64isa.NewPutInstructionDefinition()}, - {Key: "term", Value: usm64isa.NewTerminateInstructionDefinition()}, - }, - false, - ) -} diff --git a/usm64/managers/registers.go b/usm64/managers/registers.go deleted file mode 100644 index 920d4f0..0000000 --- a/usm64/managers/registers.go +++ /dev/null @@ -1,42 +0,0 @@ -package usm64managers - -import ( - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" -) - -type RegisterMap map[string]*gen.RegisterInfo - -func (m *RegisterMap) GetRegister(name string) *gen.RegisterInfo { - val, ok := (*m)[name] - if !ok { - return nil - } - return val -} - -func (m *RegisterMap) NewRegister(reg *gen.RegisterInfo) core.ResultList { - (*m)[reg.Name] = reg - return core.ResultList{} -} - -func (m *RegisterMap) DeleteRegister(reg *gen.RegisterInfo) core.ResultList { - delete(*m, reg.Name) - return core.ResultList{} -} - -func (m *RegisterMap) Size() int { - return len(*m) -} - -func (m *RegisterMap) GetAllRegisters() []*gen.RegisterInfo { - registers := make([]*gen.RegisterInfo, 0, len(*m)) - for _, reg := range *m { - registers = append(registers, reg) - } - return registers -} - -func NewRegisterManager(*gen.FileGenerationContext) gen.RegisterManager { - return &RegisterMap{} -} diff --git a/usm64/managers/types.go b/usm64/managers/types.go deleted file mode 100644 index 8476ca0..0000000 --- a/usm64/managers/types.go +++ /dev/null @@ -1,35 +0,0 @@ -package usm64managers - -import ( - "math/big" - - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" -) - -type TypeMap struct { - BaseType *gen.NamedTypeInfo -} - -func (m *TypeMap) GetType(name string) *gen.NamedTypeInfo { - if name == m.BaseType.Name { - return m.BaseType - } else { - return nil - } -} - -func (m *TypeMap) NewType(*gen.NamedTypeInfo) core.Result { - return core.Result{ - { - Type: core.ErrorResult, - Message: "Type declaration not supported in usm64", - }, - } -} - -func NewTypeManager(*gen.GenerationContext) gen.TypeManager { - return &TypeMap{ - BaseType: gen.NewNamedTypeInfo("$64", big.NewInt(64), nil), - } -} diff --git a/usm64/ssa/ssa_construction.go b/usm64/ssa/ssa_construction.go deleted file mode 100644 index f4b4172..0000000 --- a/usm64/ssa/ssa_construction.go +++ /dev/null @@ -1,121 +0,0 @@ -package usm64ssa - -import ( - "fmt" - - "alon.kr/x/usm/core" - "alon.kr/x/usm/gen" - "alon.kr/x/usm/opt/ssa" - usm64isa "alon.kr/x/usm/usm64/isa" -) - -type ConstructionScheme struct { - RenamesPerRegister map[*gen.RegisterInfo]uint -} - -func NewConstructionScheme() ssa.SsaConstructionScheme { - scheme := &ConstructionScheme{ - RenamesPerRegister: make(map[*gen.RegisterInfo]uint), - } - - return ssa.SsaConstructionScheme(scheme) -} - -func (s *ConstructionScheme) NewPhiInstruction( - block *gen.BasicBlockInfo, - register *gen.RegisterInfo, -) (ssa.PhiInstruction, core.ResultList) { - info := gen.NewEmptyInstructionInfo(nil) - target := gen.NewTargetInfo(register) - info.AppendTarget(&target) - instruction, results := usm64isa.NewPhiInstruction(info) - info.SetInstruction(instruction) - block.PrependInstruction(info) - return ssa.PhiInstruction(instruction), results -} - -func (s *ConstructionScheme) NewRenamedRegister( - register *gen.RegisterInfo, -) *gen.RegisterInfo { - renamedNumber := s.RenamesPerRegister[register] - s.RenamesPerRegister[register]++ - renamedName := register.Name + "_" + fmt.Sprint(renamedNumber) - return gen.NewRegisterInfo(renamedName, register.Type) -} - -func (s *ConstructionScheme) renameArgument( - argument gen.ArgumentInfo, - reachingSet ssa.ReachingDefinitionsSet, -) core.ResultList { - if argument, ok := argument.(*gen.RegisterArgumentInfo); ok { - baseRegister := argument.Register - renamedRegister := reachingSet.GetReachingDefinition(baseRegister) - argument.SwitchRegister(renamedRegister) - } - - return core.ResultList{} -} - -func (s *ConstructionScheme) renameTarget( - instruction *gen.InstructionInfo, - target *gen.TargetInfo, - reachingSet ssa.ReachingDefinitionsSet, -) core.ResultList { - baseRegister := target.Register - renamedRegister := reachingSet.RenameDefinitionRegister(baseRegister) - instruction.SwitchTarget(target, renamedRegister) - return core.ResultList{} -} - -func (s *ConstructionScheme) renameInstruction( - instruction *gen.InstructionInfo, - reachingSet ssa.ReachingDefinitionsSet, -) core.ResultList { - // First, we rename the arguments. - for _, argument := range instruction.Arguments { - results := s.renameArgument(argument, reachingSet) - if !results.IsEmpty() { - return results - } - } - - // Then, we define the new registers (targets). - for _, target := range instruction.Targets { - results := s.renameTarget(instruction, target, reachingSet) - if !results.IsEmpty() { - return results - } - } - - return core.ResultList{} -} - -func (s *ConstructionScheme) RenameBasicBlock( - block *gen.BasicBlockInfo, - reachingSet ssa.ReachingDefinitionsSet, -) core.ResultList { - for _, instruction := range block.Instructions { - results := s.renameInstruction(instruction, reachingSet) - if !results.IsEmpty() { - return results - } - } - - return core.ResultList{} -} - -func ConvertToSsaForm(function *gen.FunctionInfo) core.ResultList { - constructionScheme := NewConstructionScheme() - ssaInfo := ssa.NewFunctionSsaInfo(function, constructionScheme) - results := ssaInfo.InsertPhiInstructions() - if !results.IsEmpty() { - return results - } - - results = ssaInfo.RenameRegisters() - if results.IsEmpty() { - return results - } - - return core.ResultList{} -} From 89dfd2dd081a74cec3f1594e0b16b6bd10574501 Mon Sep 17 00:00:00 2001 From: Alon Krymgand Date: Mon, 2 Jun 2025 17:58:08 +0000 Subject: [PATCH 33/33] Update gen/assert.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- gen/assert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gen/assert.go b/gen/assert.go index da16367..9641c70 100644 --- a/gen/assert.go +++ b/gen/assert.go @@ -127,7 +127,7 @@ func AssertBigIntInSet( fail: message := "Expected one of " message += "#" + strconv.FormatInt(options[0], 10) - for _, option := range options { + for _, option := range options[1:] { message += ", #" + strconv.FormatInt(option, 10) }