go/src/cmd/compile/internal/ssa/config.go
Keith Randall 2a5e6c47bc [dev.ssa] cmd/compile/internal/ssa: redo how sign extension is handled
For integer types less than a machine register, we have to decide
what the invariants are for the high bits of the register.  We used
to set the high bits to the correct extension (sign or zero, as
determined by the type) of the low bits.

This CL makes the compiler ignore the high bits of the register
altogether (they are junk).

On this plus side, this means ops that generate subword results don't
have to worry about correctly extending them.  On the minus side,
ops that consume subword arguments have to deal with the input
registers not being correctly extended.

For x86, this tradeoff is probably worth it.  Almost all opcodes
have versions that use only the correct subword piece of their
inputs.  (The one big exception is array indexing.)  Not many opcodes
can correctly sign extend on output.

For other architectures, the tradeoff is probably not so clear, as
they don't have many subword-safe opcodes (e.g. 16-bit compare,
ignoring the high 16/48 bits).  Fortunately we can decide whether
we do this per-architecture.

For the machine-independent opcodes, we pretend that the "register"
size is equal to the type width, so sign extension is immaterial.
Opcodes that care about the signedness of the input (e.g. compare,
right shift) have two different variants.

Change-Id: I465484c5734545ee697afe83bc8bf4b53bd9df8d
Reviewed-on: https://go-review.googlesource.com/12600
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2015-07-28 19:09:36 +00:00

79 lines
2.4 KiB
Go

// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssa
type Config struct {
arch string // "amd64", etc.
IntSize int64 // 4 or 8
PtrSize int64 // 4 or 8
Uintptr Type // pointer arithmetic type
Int Type
lowerBlock func(*Block) bool // lowering function
lowerValue func(*Value, *Config) bool // lowering function
fe Frontend // callbacks into compiler frontend
// TODO: more stuff. Compiler flags of interest, ...
}
type Frontend interface {
// StringData returns a symbol pointing to the given string's contents.
StringData(string) interface{} // returns *gc.Sym
// Log logs a message from the compiler.
Logf(string, ...interface{})
// Fatal reports a compiler error and exits.
Fatalf(string, ...interface{})
// Unimplemented reports that the function cannot be compiled.
// It will be removed once SSA work is complete.
Unimplementedf(msg string, args ...interface{})
}
// NewConfig returns a new configuration object for the given architecture.
func NewConfig(arch string, fe Frontend) *Config {
c := &Config{arch: arch, fe: fe}
switch arch {
case "amd64":
c.IntSize = 8
c.PtrSize = 8
c.lowerBlock = rewriteBlockAMD64
c.lowerValue = rewriteValueAMD64
case "386":
c.IntSize = 4
c.PtrSize = 4
c.lowerBlock = rewriteBlockAMD64
c.lowerValue = rewriteValueAMD64 // TODO(khr): full 32-bit support
default:
fe.Unimplementedf("arch %s not implemented", arch)
}
// cache the frequently-used types in the config
c.Uintptr = TypeUInt32
c.Int = TypeInt32
if c.PtrSize == 8 {
c.Uintptr = TypeUInt64
}
if c.IntSize == 8 {
c.Int = TypeInt64
}
return c
}
func (c *Config) Frontend() Frontend { return c.fe }
// NewFunc returns a new, empty function object
func (c *Config) NewFunc() *Func {
// TODO(khr): should this function take name, type, etc. as arguments?
return &Func{Config: c}
}
func (c *Config) Logf(msg string, args ...interface{}) { c.fe.Logf(msg, args...) }
func (c *Config) Fatalf(msg string, args ...interface{}) { c.fe.Fatalf(msg, args...) }
func (c *Config) Unimplementedf(msg string, args ...interface{}) { c.fe.Unimplementedf(msg, args...) }
// TODO(khr): do we really need a separate Config, or can we just
// store all its fields inside a Func?