operand: const types

This commit is contained in:
Michael McLoughlin
2018-12-26 16:42:39 -08:00
parent b3644ec7fc
commit abd300c0e9
7 changed files with 227 additions and 24 deletions

View File

@@ -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
}

32
operand/const.go Normal file
View File

@@ -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)
}

32
operand/const_test.go Normal file
View File

@@ -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)
}
}
}

75
operand/make_const.go Normal file
View File

@@ -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)
}
}

View File

@@ -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")

75
operand/zconst.go Normal file
View File

@@ -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() {}