528 lines
16 KiB
Go
528 lines
16 KiB
Go
package llvm
|
|
|
|
import "fmt"
|
|
import "log"
|
|
import "math"
|
|
import "strings"
|
|
import "math/big"
|
|
|
|
import "github.com/mewmew/float"
|
|
import "github.com/mewmew/float/binary128"
|
|
import "github.com/mewmew/float/binary16"
|
|
import "github.com/mewmew/float/float128ppc"
|
|
import "github.com/mewmew/float/float80x86"
|
|
|
|
type Const interface {
|
|
Value
|
|
IsConstant ()
|
|
}
|
|
|
|
type ConstArray struct {
|
|
Ty *TypeArray
|
|
Elements []Const
|
|
}
|
|
func (*ConstArray) IsConstant () { }
|
|
func (this *ConstArray) Type () Type { return this.Ty }
|
|
func (this *ConstArray) String () string {
|
|
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
|
|
}
|
|
func (this *ConstArray) Name () string { return this.Identifier() }
|
|
func (this *ConstArray) Identifier () string {
|
|
buf := &strings.Builder{}
|
|
buf.WriteString("[")
|
|
for i, elem := range this.Elements {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(elem.String())
|
|
}
|
|
buf.WriteString("]")
|
|
return buf.String()
|
|
}
|
|
|
|
type ConstBlockAddress struct {
|
|
Function Const
|
|
Block Value
|
|
}
|
|
func (*ConstBlockAddress) IsConstant ( ) { }
|
|
func (this *ConstBlockAddress) Type () Type { return &TypePointer { } }
|
|
func (this *ConstBlockAddress) String () string {
|
|
return fmt.Sprintf("%v %v", &TypePointer { }, this.Identifier())
|
|
}
|
|
func (this *ConstBlockAddress) Name () string { return this.Identifier() }
|
|
func (this *ConstBlockAddress) Identifier () string {
|
|
return fmt.Sprintf("blockaddress(%s, %s)", this.Function.Name(), this.Block.Name())
|
|
}
|
|
|
|
type ConstCharArray struct {
|
|
Ty *TypeArray
|
|
Elements []byte
|
|
}
|
|
func (*ConstCharArray) IsConstant () { }
|
|
func (this *ConstCharArray) Type () Type { return this.Ty }
|
|
func (this *ConstCharArray) String () string {
|
|
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
|
|
}
|
|
func (this *ConstCharArray) Name () string { return this.Identifier() }
|
|
func (this *ConstCharArray) Identifier () string {
|
|
return "c" + EscapeQuoteString(this.Elements)
|
|
}
|
|
|
|
type ConstDSOLocalEquivalent struct {
|
|
Function Const
|
|
}
|
|
func (*ConstDSOLocalEquivalent) IsConstant () { }
|
|
func (this *ConstDSOLocalEquivalent) Type () Type { return this.Function.Type() }
|
|
func (this *ConstDSOLocalEquivalent) String () string {
|
|
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
|
|
}
|
|
func (this *ConstDSOLocalEquivalent) Name () string { return this.Identifier() }
|
|
func (this *ConstDSOLocalEquivalent) Identifier () string {
|
|
return fmt.Sprintf("dso_local_equivalent %s", this.Function.Name())
|
|
}
|
|
|
|
type ConstFloat struct {
|
|
Ty *TypeFloat
|
|
Value *big.Float
|
|
NaN bool
|
|
}
|
|
func NewConstFloat (ty *TypeFloat, value float64) *ConstFloat {
|
|
if math.IsNaN(value) {
|
|
f := &ConstFloat { Ty: ty, Value: &big.Float { }, NaN: true }
|
|
// Store sign of NaN.
|
|
if math.Signbit(value) {
|
|
f.Value.SetFloat64(-1)
|
|
}
|
|
return f
|
|
}
|
|
return &ConstFloat {
|
|
Ty: ty,
|
|
Value: big.NewFloat(value),
|
|
}
|
|
}
|
|
func (*ConstFloat) IsConstant () { }
|
|
func (this *ConstFloat) Type () Type { return this.Ty }
|
|
func (this *ConstFloat) Name () string { return this.Identifier() }
|
|
func (this *ConstFloat) String () string {
|
|
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
|
|
}
|
|
func (this *ConstFloat) Identifier () string {
|
|
// FloatLit
|
|
//
|
|
// Print hexadecimal representation of floating-point literal if NaN, Inf,
|
|
// inexact or extended precision (x86_fp80, fp128 or ppc_fp128).
|
|
switch this.Ty.Kind {
|
|
// half (IEEE 754 half precision)
|
|
case FloatKindHalf:
|
|
const hexPrefix = 'H'
|
|
if this.NaN {
|
|
bits := binary16.NaN.Bits()
|
|
if this.Value != nil && this.Value.Signbit() {
|
|
bits = binary16.NegNaN.Bits()
|
|
}
|
|
return fmt.Sprintf("0x%c%04X", hexPrefix, bits)
|
|
}
|
|
if this.Value.IsInf() || !float.IsExact16(this.Value) {
|
|
f, acc := binary16.NewFromBig(this.Value)
|
|
if acc != big.Exact {
|
|
log.Printf("unable to represent floating-point constant %v of type %v exactly; please submit a bug report to fspl with this error message", this.Value, this.Type())
|
|
}
|
|
bits := f.Bits()
|
|
return fmt.Sprintf("0x%c%04X", hexPrefix, bits)
|
|
}
|
|
// c is representable without loss as floating-point literal, this case is
|
|
// handled for half, float and double below the switch statement.
|
|
// float (IEEE 754 single precision)
|
|
case FloatKindFloat:
|
|
// ref: https://groups.google.com/d/msg/llvm-dev/IlqV3TbSk6M/27dAggZOMb0J
|
|
//
|
|
// The exact bit representation of the float is laid out with the
|
|
// corresponding bitwise representation of a double: the sign bit is
|
|
// copied over, the exponent is encoded in the larger width, and the 23
|
|
// bits of significand fills in the top 23 bits of significand in the
|
|
// double. A double has 52 bits of significand, so this means that the
|
|
// last 29 bits of significand will always be ignored. As an error
|
|
// detection measure, the IR parser requires them to be zero.
|
|
if this.NaN {
|
|
f := math.NaN()
|
|
if this.Value != nil && this.Value.Signbit() {
|
|
f = math.Copysign(f, -1)
|
|
}
|
|
bits := math.Float64bits(f)
|
|
// zero out last 29 bits.
|
|
bits &^= 0x1FFFFFFF
|
|
return fmt.Sprintf("0x%X", bits)
|
|
}
|
|
if this.Value.IsInf() || !float.IsExact32(this.Value) {
|
|
f, _ := this.Value.Float64()
|
|
bits := math.Float64bits(f)
|
|
// Note, to match Clang output we do not zero-pad the hexadecimal
|
|
// output.
|
|
// zero out last 29 bits.
|
|
bits &^= 0x1FFFFFFF
|
|
return fmt.Sprintf("0x%X", bits)
|
|
}
|
|
// c is representable without loss as floating-point literal, this case is
|
|
// handled for half, float and double below the switch statement.
|
|
// double (IEEE 754 double precision)
|
|
case FloatKindDouble:
|
|
if this.NaN {
|
|
// To use the same cannonical representation as LLVM IR for NaN values, we
|
|
// explicitly a qNaN value (quiet NaN) with the leading bit in the mantissa
|
|
// set, rather than the trailing bit as used for the cannonical
|
|
// representation in Go (see math.NaN).
|
|
//
|
|
// For further background, see https://github.com/llir/llvm/issues/133
|
|
//
|
|
// exponent mantissa
|
|
// s 11111111111 1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx = quiet (qNaN)
|
|
// s 11111111111 0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx = signaling (sNaN) **
|
|
// ^ quiet bit
|
|
//
|
|
// Where ** denote that at least one of the 'x' bits has to be set, since the
|
|
// mantissa must be non-zero to denote NaN.
|
|
//
|
|
// quiet NaN:
|
|
// 0x7FF8000000000000 = 0b0_11111111111_100000000000000000000000000000000000000000000000000
|
|
f := math.Float64frombits(0x7FF8000000000000) // quiet NaN
|
|
if this.Value != nil && this.Value.Signbit() {
|
|
f = math.Copysign(f, -1)
|
|
}
|
|
bits := math.Float64bits(f)
|
|
return fmt.Sprintf("0x%X", bits)
|
|
}
|
|
if this.Value.IsInf() || !float.IsExact64(this.Value) {
|
|
f, _ := this.Value.Float64()
|
|
bits := math.Float64bits(f)
|
|
// Note, to match Clang output we do not zero-pad the hexadecimal
|
|
// output.
|
|
return fmt.Sprintf("0x%X", bits)
|
|
}
|
|
// c is representable without loss as floating-point literal, this case is
|
|
// handled for half, float and double below the switch statement.
|
|
// x86_fp80 (x86 extended precision)
|
|
case FloatKindX86_FP80:
|
|
// always represent x86_fp80 in hexadecimal floating-point notation.
|
|
const hexPrefix = 'K'
|
|
if this.NaN {
|
|
se, m := float80x86.NaN.Bits()
|
|
if this.Value != nil && this.Value.Signbit() {
|
|
se, m = float80x86.NegNaN.Bits()
|
|
}
|
|
return fmt.Sprintf("0x%c%04X%016X", hexPrefix, se, m)
|
|
}
|
|
f, acc := float80x86.NewFromBig(this.Value)
|
|
if acc != big.Exact {
|
|
log.Printf("unable to represent floating-point constant %v of type %v exactly; please submit a bug report to fspl with this error message", this.Value, this.Type())
|
|
}
|
|
se, m := f.Bits()
|
|
return fmt.Sprintf("0x%c%04X%016X", hexPrefix, se, m)
|
|
// fp128 (IEEE 754 quadruple precision)
|
|
case FloatKindFP128:
|
|
// always represent fp128 in hexadecimal floating-point notation.
|
|
const hexPrefix = 'L'
|
|
if this.NaN {
|
|
a, b := binary128.NaN.Bits()
|
|
if this.Value != nil && this.Value.Signbit() {
|
|
a, b = binary128.NegNaN.Bits()
|
|
}
|
|
return fmt.Sprintf("0x%c%016X%016X", hexPrefix, a, b)
|
|
}
|
|
f, acc := binary128.NewFromBig(this.Value)
|
|
if acc != big.Exact {
|
|
log.Printf("unable to represent floating-point constant %v of type %v exactly; please submit a bug report to fspl with this error message", this.Value, this.Type())
|
|
}
|
|
a, b := f.Bits()
|
|
return fmt.Sprintf("0x%c%016X%016X", hexPrefix, a, b)
|
|
// ppc_fp128 (PowerPC double-double arithmetic)
|
|
case FloatKindPPC_FP128:
|
|
// always represent ppc_fp128 in hexadecimal floating-point notation.
|
|
const hexPrefix = 'M'
|
|
if this.NaN {
|
|
a, b := float128ppc.NaN.Bits()
|
|
if this.Value != nil && this.Value.Signbit() {
|
|
a, b = float128ppc.NegNaN.Bits()
|
|
}
|
|
return fmt.Sprintf("0x%c%016X%016X", hexPrefix, a, b)
|
|
}
|
|
f, acc := float128ppc.NewFromBig(this.Value)
|
|
if acc != big.Exact {
|
|
log.Printf("unable to represent floating-point constant %v of type %v exactly; please submit a bug report to fspl with this error message", this.Value, this.Type())
|
|
}
|
|
a, b := f.Bits()
|
|
return fmt.Sprintf("0x%c%016X%016X", hexPrefix, a, b)
|
|
default:
|
|
panic(fmt.Errorf("support for floating-point kind %v not yet implemented", this.Ty.Kind))
|
|
}
|
|
// Insert decimal point if not present.
|
|
// 3e4 -> 3.0e4
|
|
// 42 -> 42.0
|
|
s := this.Value.Text('g', -1)
|
|
if !strings.ContainsRune(s, '.') {
|
|
if pos := strings.IndexByte(s, 'e'); pos != -1 {
|
|
s = s[:pos] + ".0" + s[pos:]
|
|
} else {
|
|
s += ".0"
|
|
}
|
|
}
|
|
return s
|
|
}
|
|
|
|
type ConstIndex struct {
|
|
Const
|
|
InRange bool
|
|
}
|
|
func (this *ConstIndex) Name () string { return this.Identifier() }
|
|
func (this *ConstIndex) Identifier () string {
|
|
// OptInrange Type Constant
|
|
if this.InRange {
|
|
return fmt.Sprintf("inrange %s", this.Const)
|
|
}
|
|
return this.Const.String()
|
|
}
|
|
|
|
type ConstInt struct {
|
|
Ty *TypeInt
|
|
Value *big.Int
|
|
}
|
|
func NewConstInt (ty *TypeInt, value int64) *ConstInt {
|
|
return &ConstInt {
|
|
Ty: ty,
|
|
Value: big.NewInt(value),
|
|
}
|
|
}
|
|
func NewConstBool (value bool) *ConstInt {
|
|
var intVal int64
|
|
if value { intVal = 1 }
|
|
return NewConstInt(I1, intVal)
|
|
}
|
|
func (*ConstInt) IsConstant () { }
|
|
func (this *ConstInt) Type () Type { return this.Ty }
|
|
func (this *ConstInt) String () string {
|
|
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
|
|
}
|
|
func (this *ConstInt) Name () string { return this.Identifier() }
|
|
func (this *ConstInt) Identifier () string {
|
|
// IntLit
|
|
if this.Ty.BitSize == 1 {
|
|
// "true"
|
|
// "false"
|
|
switch x := this.Value.Int64(); x {
|
|
case 0:
|
|
return "false"
|
|
case 1:
|
|
return "true"
|
|
default:
|
|
panic(fmt.Errorf("invalid integer value of boolean type; expected 0 or 1, got %d", x))
|
|
}
|
|
}
|
|
// Output x in hexadecimal notation if x is positive, greater than or equal
|
|
// to 0x1000 and has a significantly lower entropy than decimal notation.
|
|
|
|
// Minimum difference between entropy of decimal and hexadecimal notation to
|
|
// output x in hexadecimal notation.
|
|
const minEntropyDiff = 0.2
|
|
// Maximum allowed entropy of hexadecimal notation to output x in hexadecimal
|
|
// notation.
|
|
//
|
|
// This is useful as some hex values, while lower entropy than their decimal
|
|
// counter-part do not improve readability.
|
|
//
|
|
// For instance, the decimal entropy of 7240739780546808700 is 9/10 = 0.9 and
|
|
// the hexadecimal entropy of 0x647C4677A2884B7C is 8/16 = 0.5. As such the
|
|
// entropy difference is 0.9-0.5 = 0.4, but the hexadecimal notation does not
|
|
// improve readability. Thus we add an upper bound on the hexadecimal entropy,
|
|
// and if the entropy is above this bound, output in decimal notation
|
|
// instead.
|
|
hexLength := len(this.Value.Text(16))
|
|
maxHexEntropy := calcMaxHexEntropy(hexLength)
|
|
threshold := big.NewInt(0x1000) // 4096
|
|
// Check entropy if x >= 0x1000.
|
|
if this.Value.Cmp(threshold) >= 0 {
|
|
hexentropy := hexEntropy(this.Value)
|
|
decentropy := decimalEntropy(this.Value)
|
|
if hexentropy <= maxHexEntropy+0.01 && decentropy >= hexentropy+minEntropyDiff {
|
|
return "u0x" + strings.ToUpper(this.Value.Text(16))
|
|
}
|
|
}
|
|
return this.Value.String()
|
|
}
|
|
func calcMaxHexEntropy (length int) float64 {
|
|
if length > 16 {
|
|
length = 16
|
|
}
|
|
switch {
|
|
case length < 4:
|
|
return 0
|
|
case 4 <= length && length <= 6:
|
|
return 2.0 / float64(length)
|
|
case 7 <= length && length <= 10:
|
|
return 3.0 / float64(length)
|
|
// length >= 11
|
|
default:
|
|
return 4.0 / float64(length)
|
|
}
|
|
}
|
|
func hexEntropy(x *big.Int) float64 {
|
|
const base = 16
|
|
return intEntropy(x, base)
|
|
}
|
|
func decimalEntropy(x *big.Int) float64 {
|
|
const base = 10
|
|
return intEntropy(x, base)
|
|
}
|
|
func intEntropy(x *big.Int, base int) float64 {
|
|
if base < 2 || base > 62 {
|
|
panic(fmt.Errorf("invalid base; expected 2 <= base <= 62, got %d", base))
|
|
}
|
|
const maxBase = 62
|
|
var digits [maxBase]bool
|
|
s := x.Text(base)
|
|
// Locate unique digits.
|
|
for i := 0; i < len(s); i++ {
|
|
b := s[i]
|
|
if b == '-' {
|
|
// skip sign.
|
|
continue
|
|
}
|
|
d := digitValue(b)
|
|
digits[d] = true
|
|
}
|
|
// Count unique digits.
|
|
uniqueDigits := 0
|
|
for i := 0; i < base; i++ {
|
|
if digits[i] {
|
|
uniqueDigits++
|
|
}
|
|
}
|
|
length := len(s)
|
|
if length > base {
|
|
length = base
|
|
}
|
|
return float64(uniqueDigits) / float64(length)
|
|
}
|
|
func digitValue(b byte) int {
|
|
switch {
|
|
case '0' <= b && b <= '9':
|
|
return 0 + int(b-'0')
|
|
case 'a' <= b && b <= 'z':
|
|
return 10 + int(b-'a')
|
|
case 'A' <= b && b <= 'Z':
|
|
return 36 + int(b-'A')
|
|
default:
|
|
panic(fmt.Errorf("invalid digit byte; expected [0-9a-zA-Z], got %#U", b))
|
|
}
|
|
}
|
|
|
|
type ConstNoCFI struct {
|
|
Function Const
|
|
}
|
|
func (*ConstNoCFI) IsConstant () { }
|
|
func (this *ConstNoCFI) Type () Type { return this.Function.Type() }
|
|
func (this *ConstNoCFI) String () string {
|
|
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
|
|
}
|
|
func (this *ConstNoCFI) Name () string { return this.Identifier() }
|
|
func (this *ConstNoCFI) Identifier () string {
|
|
return fmt.Sprintf("no_cfi %s", this.Function.Identifier())
|
|
}
|
|
|
|
type ConstNull struct { }
|
|
func (*ConstNull) IsConstant () { }
|
|
func (*ConstNull) Type () Type { return &TypePointer { } }
|
|
func (this *ConstNull) String () string {
|
|
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
|
|
}
|
|
func (this *ConstNull) Name () string { return this.Identifier() }
|
|
func (this *ConstNull) Identifier () string { return "null" }
|
|
|
|
type ConstPoison struct {
|
|
Ty Type
|
|
}
|
|
func (*ConstPoison) IsConstant () { }
|
|
func (this *ConstPoison) Type () Type { return this.Ty }
|
|
func (this *ConstPoison) String () string {
|
|
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
|
|
}
|
|
func (this *ConstPoison) Name () string { return this.Identifier() }
|
|
func (this *ConstPoison) Identifier () string { return "poison" }
|
|
|
|
type ConstStruct struct {
|
|
Ty *TypeStruct
|
|
Fields []Const
|
|
}
|
|
func (*ConstStruct) IsConstant () { }
|
|
func (this *ConstStruct) Type () Type { return this.Ty }
|
|
func (this *ConstStruct) String () string {
|
|
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
|
|
}
|
|
func (this *ConstStruct) Name () string { return this.Identifier() }
|
|
func (this *ConstStruct) Identifier () string {
|
|
buf := &strings.Builder{}
|
|
if this.Ty.Packed {
|
|
buf.WriteString("<")
|
|
}
|
|
buf.WriteString("{ ")
|
|
for i, field := range this.Fields {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(field.String())
|
|
}
|
|
buf.WriteString(" }")
|
|
if this.Ty.Packed {
|
|
buf.WriteString(">")
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
type ConstUndef struct {
|
|
Ty Type
|
|
}
|
|
func (*ConstUndef) IsConstant () { }
|
|
func (this *ConstUndef) Type () Type { return this.Ty }
|
|
func (this *ConstUndef) String () string {
|
|
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
|
|
}
|
|
func (this *ConstUndef) Name () string { return this.Identifier() }
|
|
func (this *ConstUndef) Identifier () string { return "undef" }
|
|
|
|
type ConstVector struct {
|
|
Ty *TypeVector
|
|
Elements []Const
|
|
}
|
|
func (*ConstVector) IsConstant () { }
|
|
func (this *ConstVector) Type () Type { return this.Ty }
|
|
func (this *ConstVector) String () string {
|
|
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
|
|
}
|
|
func (this *ConstVector) Name () string { return this.Identifier() }
|
|
func (this *ConstVector) Identifier () string {
|
|
buf := &strings.Builder{}
|
|
buf.WriteString("<")
|
|
for i, elem := range this.Elements {
|
|
if i != 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(elem.String())
|
|
}
|
|
buf.WriteString(">")
|
|
return buf.String()
|
|
}
|
|
|
|
type ConstZeroInitializer struct {
|
|
Ty Type
|
|
}
|
|
func NewConstZeroInitializer (ty Type) *ConstZeroInitializer {
|
|
return &ConstZeroInitializer {
|
|
Ty: ty,
|
|
}
|
|
}
|
|
func (*ConstZeroInitializer) IsConstant () { }
|
|
func (this *ConstZeroInitializer) Type () Type { return this.Ty }
|
|
func (this *ConstZeroInitializer) String () string {
|
|
return fmt.Sprintf("%v %v", this.Type(), this.Identifier())
|
|
}
|
|
func (this *ConstZeroInitializer) Name () string { return this.Identifier() }
|
|
func (this *ConstZeroInitializer) Identifier () string { return "zeroinitializer" }
|