pass: VerifyMemOperands (#127)
Introduces a pass to verify memory operands. This is added to a new Verify pass, that in future could do much more. Updates #125
This commit is contained in:
committed by
GitHub
parent
ff7a160610
commit
126469f13d
17
pass/pass.go
17
pass/pass.go
@@ -11,6 +11,7 @@ import (
|
|||||||
// Compile pass compiles an avo file. Upon successful completion the avo file
|
// Compile pass compiles an avo file. Upon successful completion the avo file
|
||||||
// may be printed to Go assembly.
|
// may be printed to Go assembly.
|
||||||
var Compile = Concat(
|
var Compile = Concat(
|
||||||
|
Verify,
|
||||||
FunctionPass(PruneJumpToFollowingLabel),
|
FunctionPass(PruneJumpToFollowingLabel),
|
||||||
FunctionPass(PruneDanglingLabels),
|
FunctionPass(PruneDanglingLabels),
|
||||||
FunctionPass(LabelTarget),
|
FunctionPass(LabelTarget),
|
||||||
@@ -51,6 +52,22 @@ func (p FunctionPass) Execute(f *ir.File) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InstructionPass is a convenience for implementing a full file pass with a
|
||||||
|
// function that operates on each Instruction independently.
|
||||||
|
type InstructionPass func(*ir.Instruction) error
|
||||||
|
|
||||||
|
// Execute calls p on every instruction in the file. Exits on the first error.
|
||||||
|
func (p InstructionPass) Execute(f *ir.File) error {
|
||||||
|
for _, fn := range f.Functions() {
|
||||||
|
for _, i := range fn.Instructions() {
|
||||||
|
if err := p(i); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Concat returns a pass that executes the given passes in order, stopping on the first error.
|
// Concat returns a pass that executes the given passes in order, stopping on the first error.
|
||||||
func Concat(passes ...Interface) Interface {
|
func Concat(passes ...Interface) Interface {
|
||||||
return Func(func(f *ir.File) error {
|
return Func(func(f *ir.File) error {
|
||||||
|
|||||||
32
pass/verify.go
Normal file
32
pass/verify.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package pass
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/mmcloughlin/avo/ir"
|
||||||
|
"github.com/mmcloughlin/avo/operand"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verify pass validates an avo file.
|
||||||
|
var Verify = Concat(
|
||||||
|
InstructionPass(VerifyMemOperands),
|
||||||
|
)
|
||||||
|
|
||||||
|
// VerifyMemOperands checks the instruction's memory operands.
|
||||||
|
func VerifyMemOperands(i *ir.Instruction) error {
|
||||||
|
for _, op := range i.Operands {
|
||||||
|
m, ok := op.(operand.Mem)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Base == nil {
|
||||||
|
return errors.New("bad memory operand: missing base register")
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Index != nil && m.Scale == 0 {
|
||||||
|
return errors.New("bad memory operand: index register with scale 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
58
pass/verify_test.go
Normal file
58
pass/verify_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package pass
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mmcloughlin/avo/ir"
|
||||||
|
"github.com/mmcloughlin/avo/operand"
|
||||||
|
"github.com/mmcloughlin/avo/reg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVerifyMemOperands(t *testing.T) {
|
||||||
|
i := &ir.Instruction{
|
||||||
|
Operands: []operand.Op{
|
||||||
|
reg.RAX,
|
||||||
|
operand.Mem{
|
||||||
|
Base: reg.R10,
|
||||||
|
Disp: 42,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := VerifyMemOperands(i); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyMemOperandsErrors(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Operands []operand.Op
|
||||||
|
ErrorSubstring string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Operands: []operand.Op{
|
||||||
|
reg.RAX,
|
||||||
|
operand.Mem{
|
||||||
|
Disp: 42,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ErrorSubstring: "missing base",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Operands: []operand.Op{
|
||||||
|
operand.Mem{
|
||||||
|
Base: reg.EBX,
|
||||||
|
Index: reg.R9L,
|
||||||
|
},
|
||||||
|
reg.ECX,
|
||||||
|
},
|
||||||
|
ErrorSubstring: "index register with scale 0",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
i := &ir.Instruction{Operands: c.Operands}
|
||||||
|
if err := VerifyMemOperands(i); err == nil || !strings.Contains(err.Error(), c.ErrorSubstring) {
|
||||||
|
t.Errorf("got error %v; expected error to contain %q", err, c.ErrorSubstring)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user