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