From abd300c0e9a8d8d441ba66958a1605f5fb4d334a Mon Sep 17 00:00:00 2001 From: Michael McLoughlin Date: Wed, 26 Dec 2018 16:42:39 -0800 Subject: [PATCH] operand: const types --- operand/checks.go | 25 ++++++--------- operand/const.go | 32 ++++++++++++++++++ operand/const_test.go | 32 ++++++++++++++++++ operand/make_const.go | 75 +++++++++++++++++++++++++++++++++++++++++++ operand/types.go | 8 +---- operand/zconst.go | 75 +++++++++++++++++++++++++++++++++++++++++++ pass/reg_test.go | 4 +-- 7 files changed, 227 insertions(+), 24 deletions(-) create mode 100644 operand/const.go create mode 100644 operand/const_test.go create mode 100644 operand/make_const.go create mode 100644 operand/zconst.go diff --git a/operand/checks.go b/operand/checks.go index 322457bc..3f4dac90 100644 --- a/operand/checks.go +++ b/operand/checks.go @@ -1,8 +1,6 @@ package operand import ( - "math" - "github.com/mmcloughlin/avo/reg" ) @@ -14,9 +12,6 @@ func IsRegister(op Op) bool { _, ok := op.(reg.Register); return ok } // IsMem returns whether op has type Mem. func IsMem(op Op) bool { _, ok := op.(Mem); return ok } -// IsImm returns whether op has type Imm. -func IsImm(op Op) bool { _, ok := op.(Imm); return ok } - // IsRel returns whether op has type Rel. func IsRel(op Op) bool { _, ok := op.(Rel); return ok } @@ -24,43 +19,43 @@ func IsRel(op Op) bool { _, ok := op.(Rel); return ok } // Is1 returns true if op is the immediate constant 1. func Is1(op Op) bool { - i, ok := op.(Imm) + i, ok := op.(U8) return ok && i == 1 } // Is3 returns true if op is the immediate constant 3. func Is3(op Op) bool { - i, ok := op.(Imm) + i, ok := op.(U8) return ok && i == 3 } // IsImm2u returns true if op is a 2-bit unsigned immediate (less than 4). func IsImm2u(op Op) bool { - i, ok := op.(Imm) + i, ok := op.(U8) return ok && i < 4 } // IsImm8 returns true is op is an 8-bit immediate. func IsImm8(op Op) bool { - i, ok := op.(Imm) - return ok && i <= math.MaxUint8 + _, ok := op.(U8) + return ok } // IsImm16 returns true is op is a 16-bit immediate. func IsImm16(op Op) bool { - i, ok := op.(Imm) - return ok && i <= math.MaxUint16 + _, ok := op.(U16) + return ok } // IsImm32 returns true is op is a 32-bit immediate. func IsImm32(op Op) bool { - i, ok := op.(Imm) - return ok && i <= math.MaxUint32 + _, ok := op.(U32) + return ok } // IsImm64 returns true is op is a 64-bit immediate. func IsImm64(op Op) bool { - _, ok := op.(Imm) + _, ok := op.(U64) return ok } diff --git a/operand/const.go b/operand/const.go new file mode 100644 index 00000000..e4652757 --- /dev/null +++ b/operand/const.go @@ -0,0 +1,32 @@ +package operand + +import "fmt" + +type Constant interface { + Op + Bytes() int + constant() +} + +//go:generate go run make_const.go -output zconst.go + +// String is a string constant. +type String string + +func (s String) Asm() string { return fmt.Sprintf("$%q", s) } +func (s String) Bytes() int { return len(s) } +func (s String) constant() {} + +// Imm returns an unsigned integer constant with size guessed from x. +func Imm(x uint64) Constant { + // TODO(mbm): remove this function + switch { + case uint64(uint8(x)) == x: + return U8(x) + case uint64(uint16(x)) == x: + return U16(x) + case uint64(uint32(x)) == x: + return U32(x) + } + return U64(x) +} diff --git a/operand/const_test.go b/operand/const_test.go new file mode 100644 index 00000000..aa4593f4 --- /dev/null +++ b/operand/const_test.go @@ -0,0 +1,32 @@ +package operand + +import "testing" + +func TestConstants(t *testing.T) { + cases := []struct { + Const Constant + Asm string + Bytes int + }{ + {F32(3.1415), "$(3.1415)", 4}, + {F64(3.1415), "$(3.1415)", 8}, + {U8(42), "$0x2a", 1}, + {U16(42), "$0x002a", 2}, + {U32(42), "$0x0000002a", 4}, + {U64(42), "$0x000000000000002a", 8}, + {I8(-42), "$-42", 1}, + {I16(-42), "$-42", 2}, + {I32(-42), "$-42", 4}, + {I64(-42), "$-42", 8}, + {String("hello"), "$\"hello\"", 5}, + {String("quot:\"q\""), "$\"quot:\\\"q\\\"\"", 8}, + } + for _, c := range cases { + if c.Const.Asm() != c.Asm { + t.Errorf("%v.Asm() = %v; expect %v", c.Const, c.Const.Asm(), c.Asm) + } + if c.Const.Bytes() != c.Bytes { + t.Errorf("%v.Bytes() = %v; expect %v", c.Const, c.Const.Bytes(), c.Bytes) + } + } +} diff --git a/operand/make_const.go b/operand/make_const.go new file mode 100644 index 00000000..21f9ab3e --- /dev/null +++ b/operand/make_const.go @@ -0,0 +1,75 @@ +// +build ignore + +package main + +import ( + "bytes" + "flag" + "fmt" + "go/format" + "io" + "log" + "os" + "path/filepath" + "runtime" + "strconv" +) + +var ( + output = flag.String("output", "", "path to output file (default stdout)") +) + +func PrintConstType(w io.Writer, name, typ, format string, size int, doc string) { + r := typ[0] + fmt.Fprintf(w, "// %s\n", doc) + fmt.Fprintf(w, "type %s %s\n", name, typ) + fmt.Fprintf(w, "\n") + fmt.Fprintf(w, "func (%c %s) Asm() string { return fmt.Sprintf(\"$%s\", %c) }\n", r, name, format, r) + fmt.Fprintf(w, "func (%c %s) Bytes() int { return %d }\n", r, name, size) + fmt.Fprintf(w, "func (%c %s) constant() {}\n", r, name) + fmt.Fprintf(w, "\n") +} + +func PrintConstTypes(w io.Writer) { + _, self, _, _ := runtime.Caller(0) + fmt.Fprintf(w, "// Code generated by %s. DO NOT EDIT.\n\n", filepath.Base(self)) + fmt.Fprintf(w, "package operand\n\n") + fmt.Fprintf(w, "import \"fmt\"\n\n") + for n := 1; n <= 8; n *= 2 { + bits := n * 8 + bs := strconv.Itoa(bits) + + if n >= 4 { + PrintConstType(w, "F"+bs, "float"+bs, "(%#v)", n, fmt.Sprintf("F%d is a %d-bit floating point constant.", bits, bits)) + } + PrintConstType(w, "I"+bs, "int"+bs, "%+d", n, fmt.Sprintf("I%d is a %d-bit signed integer constant.", bits, bits)) + PrintConstType(w, "U"+bs, "uint"+bs, "%#0"+strconv.Itoa(2*n)+"x", n, fmt.Sprintf("U%d is a %d-bit unsigned integer constant.", bits, bits)) + } +} + +func main() { + flag.Parse() + + w := os.Stdout + if *output != "" { + f, err := os.Create(*output) + if err != nil { + log.Fatal(err) + } + defer f.Close() + w = f + } + + buf := bytes.NewBuffer(nil) + PrintConstTypes(buf) + + src, err := format.Source(buf.Bytes()) + if err != nil { + log.Fatal(err) + } + + _, err = w.Write(src) + if err != nil { + log.Fatal(err) + } +} diff --git a/operand/types.go b/operand/types.go index e655af12..3e0c3cd1 100644 --- a/operand/types.go +++ b/operand/types.go @@ -75,12 +75,6 @@ func (m Mem) Asm() string { return a } -type Imm uint64 - -func (i Imm) Asm() string { - return fmt.Sprintf("$%#x", i) -} - // Rel is an offset relative to the instruction pointer. type Rel int32 @@ -109,7 +103,7 @@ func Registers(op Op) []reg.Register { r = append(r, op.Index) } return r - case Imm, Rel, LabelRef: + case Constant, Rel, LabelRef: return nil } panic("unknown operand type") diff --git a/operand/zconst.go b/operand/zconst.go new file mode 100644 index 00000000..324b4a96 --- /dev/null +++ b/operand/zconst.go @@ -0,0 +1,75 @@ +// Code generated by make_const.go. DO NOT EDIT. + +package operand + +import "fmt" + +// I8 is a 8-bit signed integer constant. +type I8 int8 + +func (i I8) Asm() string { return fmt.Sprintf("$%+d", i) } +func (i I8) Bytes() int { return 1 } +func (i I8) constant() {} + +// U8 is a 8-bit unsigned integer constant. +type U8 uint8 + +func (u U8) Asm() string { return fmt.Sprintf("$%#02x", u) } +func (u U8) Bytes() int { return 1 } +func (u U8) constant() {} + +// I16 is a 16-bit signed integer constant. +type I16 int16 + +func (i I16) Asm() string { return fmt.Sprintf("$%+d", i) } +func (i I16) Bytes() int { return 2 } +func (i I16) constant() {} + +// U16 is a 16-bit unsigned integer constant. +type U16 uint16 + +func (u U16) Asm() string { return fmt.Sprintf("$%#04x", u) } +func (u U16) Bytes() int { return 2 } +func (u U16) constant() {} + +// F32 is a 32-bit floating point constant. +type F32 float32 + +func (f F32) Asm() string { return fmt.Sprintf("$(%#v)", f) } +func (f F32) Bytes() int { return 4 } +func (f F32) constant() {} + +// I32 is a 32-bit signed integer constant. +type I32 int32 + +func (i I32) Asm() string { return fmt.Sprintf("$%+d", i) } +func (i I32) Bytes() int { return 4 } +func (i I32) constant() {} + +// U32 is a 32-bit unsigned integer constant. +type U32 uint32 + +func (u U32) Asm() string { return fmt.Sprintf("$%#08x", u) } +func (u U32) Bytes() int { return 4 } +func (u U32) constant() {} + +// F64 is a 64-bit floating point constant. +type F64 float64 + +func (f F64) Asm() string { return fmt.Sprintf("$(%#v)", f) } +func (f F64) Bytes() int { return 8 } +func (f F64) constant() {} + +// I64 is a 64-bit signed integer constant. +type I64 int64 + +func (i I64) Asm() string { return fmt.Sprintf("$%+d", i) } +func (i I64) Bytes() int { return 8 } +func (i I64) constant() {} + +// U64 is a 64-bit unsigned integer constant. +type U64 uint64 + +func (u U64) Asm() string { return fmt.Sprintf("$%#016x", u) } +func (u U64) Bytes() int { return 8 } +func (u U64) constant() {} diff --git a/pass/reg_test.go b/pass/reg_test.go index 38a735fc..901a2cd2 100644 --- a/pass/reg_test.go +++ b/pass/reg_test.go @@ -18,8 +18,8 @@ func TestLivenessBasic(t *testing.T) { ctx.Function("add") a := ctx.GP64v() b := ctx.GP64v() - ctx.MOVQ(operand.Imm(1), a) - ctx.MOVQ(operand.Imm(2), b) + ctx.MOVQ(operand.U64(1), a) + ctx.MOVQ(operand.U64(2), b) ctx.ADDQ(a, b) AssertLiveness(t, ctx,