to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. llvm-svn: 351636
638 lines
20 KiB
Go
638 lines
20 KiB
Go
//===- value.go - govalue and operations ----------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines the govalue type, which combines an LLVM value with its Go
|
|
// type, and implements various basic operations on govalues.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
package irgen
|
|
|
|
import (
|
|
"fmt"
|
|
"go/token"
|
|
|
|
"llvm.org/llgo/third_party/gotools/go/exact"
|
|
"llvm.org/llgo/third_party/gotools/go/types"
|
|
"llvm.org/llvm/bindings/go/llvm"
|
|
)
|
|
|
|
// govalue contains an LLVM value and a Go type,
|
|
// representing the result of a Go expression.
|
|
type govalue struct {
|
|
value llvm.Value
|
|
typ types.Type
|
|
}
|
|
|
|
func (v *govalue) String() string {
|
|
return fmt.Sprintf("[llgo.govalue typ:%s value:%v]", v.typ, v.value)
|
|
}
|
|
|
|
// Create a new dynamic value from a (LLVM Value, Type) pair.
|
|
func newValue(v llvm.Value, t types.Type) *govalue {
|
|
return &govalue{v, t}
|
|
}
|
|
|
|
// TODO(axw) remove this, use .typ directly
|
|
func (v *govalue) Type() types.Type {
|
|
return v.typ
|
|
}
|
|
|
|
// newValueFromConst converts a constant value to an LLVM value.
|
|
func (fr *frame) newValueFromConst(v exact.Value, typ types.Type) *govalue {
|
|
switch {
|
|
case v == nil:
|
|
llvmtyp := fr.types.ToLLVM(typ)
|
|
return newValue(llvm.ConstNull(llvmtyp), typ)
|
|
|
|
case isString(typ):
|
|
if isUntyped(typ) {
|
|
typ = types.Typ[types.String]
|
|
}
|
|
llvmtyp := fr.types.ToLLVM(typ)
|
|
strval := exact.StringVal(v)
|
|
strlen := len(strval)
|
|
i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
|
|
var ptr llvm.Value
|
|
if strlen > 0 {
|
|
init := llvm.ConstString(strval, false)
|
|
ptr = llvm.AddGlobal(fr.module.Module, init.Type(), "")
|
|
ptr.SetInitializer(init)
|
|
ptr.SetLinkage(llvm.InternalLinkage)
|
|
ptr = llvm.ConstBitCast(ptr, i8ptr)
|
|
} else {
|
|
ptr = llvm.ConstNull(i8ptr)
|
|
}
|
|
len_ := llvm.ConstInt(fr.types.inttype, uint64(strlen), false)
|
|
llvmvalue := llvm.Undef(llvmtyp)
|
|
llvmvalue = llvm.ConstInsertValue(llvmvalue, ptr, []uint32{0})
|
|
llvmvalue = llvm.ConstInsertValue(llvmvalue, len_, []uint32{1})
|
|
return newValue(llvmvalue, typ)
|
|
|
|
case isInteger(typ):
|
|
if isUntyped(typ) {
|
|
typ = types.Typ[types.Int]
|
|
}
|
|
llvmtyp := fr.types.ToLLVM(typ)
|
|
var llvmvalue llvm.Value
|
|
if isUnsigned(typ) {
|
|
v, _ := exact.Uint64Val(v)
|
|
llvmvalue = llvm.ConstInt(llvmtyp, v, false)
|
|
} else {
|
|
v, _ := exact.Int64Val(v)
|
|
llvmvalue = llvm.ConstInt(llvmtyp, uint64(v), true)
|
|
}
|
|
return newValue(llvmvalue, typ)
|
|
|
|
case isBoolean(typ):
|
|
if isUntyped(typ) {
|
|
typ = types.Typ[types.Bool]
|
|
}
|
|
return newValue(boolLLVMValue(exact.BoolVal(v)), typ)
|
|
|
|
case isFloat(typ):
|
|
if isUntyped(typ) {
|
|
typ = types.Typ[types.Float64]
|
|
}
|
|
llvmtyp := fr.types.ToLLVM(typ)
|
|
floatval, _ := exact.Float64Val(v)
|
|
llvmvalue := llvm.ConstFloat(llvmtyp, floatval)
|
|
return newValue(llvmvalue, typ)
|
|
|
|
case typ == types.Typ[types.UnsafePointer]:
|
|
llvmtyp := fr.types.ToLLVM(typ)
|
|
v, _ := exact.Uint64Val(v)
|
|
llvmvalue := llvm.ConstInt(fr.types.inttype, v, false)
|
|
llvmvalue = llvm.ConstIntToPtr(llvmvalue, llvmtyp)
|
|
return newValue(llvmvalue, typ)
|
|
|
|
case isComplex(typ):
|
|
if isUntyped(typ) {
|
|
typ = types.Typ[types.Complex128]
|
|
}
|
|
llvmtyp := fr.types.ToLLVM(typ)
|
|
floattyp := llvmtyp.StructElementTypes()[0]
|
|
llvmvalue := llvm.ConstNull(llvmtyp)
|
|
realv := exact.Real(v)
|
|
imagv := exact.Imag(v)
|
|
realfloatval, _ := exact.Float64Val(realv)
|
|
imagfloatval, _ := exact.Float64Val(imagv)
|
|
llvmre := llvm.ConstFloat(floattyp, realfloatval)
|
|
llvmim := llvm.ConstFloat(floattyp, imagfloatval)
|
|
llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmre, []uint32{0})
|
|
llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmim, []uint32{1})
|
|
return newValue(llvmvalue, typ)
|
|
}
|
|
|
|
// Special case for string -> [](byte|rune)
|
|
if u, ok := typ.Underlying().(*types.Slice); ok && isInteger(u.Elem()) {
|
|
if v.Kind() == exact.String {
|
|
strval := fr.newValueFromConst(v, types.Typ[types.String])
|
|
return fr.convert(strval, typ)
|
|
}
|
|
}
|
|
|
|
panic(fmt.Sprintf("unhandled: t=%s(%T), v=%v(%T)", typ, typ, v, v))
|
|
}
|
|
|
|
func (fr *frame) binaryOp(lhs *govalue, op token.Token, rhs *govalue) *govalue {
|
|
if op == token.NEQ {
|
|
result := fr.binaryOp(lhs, token.EQL, rhs)
|
|
return fr.unaryOp(result, token.NOT)
|
|
}
|
|
|
|
var result llvm.Value
|
|
b := fr.builder
|
|
|
|
switch typ := lhs.typ.Underlying().(type) {
|
|
case *types.Struct:
|
|
// TODO(axw) use runtime equality algorithm (will be suitably inlined).
|
|
// For now, we use compare all fields unconditionally and bitwise AND
|
|
// to avoid branching (i.e. so we don't create additional blocks).
|
|
value := newValue(boolLLVMValue(true), types.Typ[types.Bool])
|
|
for i := 0; i < typ.NumFields(); i++ {
|
|
t := typ.Field(i).Type()
|
|
lhs := newValue(b.CreateExtractValue(lhs.value, i, ""), t)
|
|
rhs := newValue(b.CreateExtractValue(rhs.value, i, ""), t)
|
|
value = fr.binaryOp(value, token.AND, fr.binaryOp(lhs, token.EQL, rhs))
|
|
}
|
|
return value
|
|
|
|
case *types.Array:
|
|
// TODO(pcc): as above.
|
|
value := newValue(boolLLVMValue(true), types.Typ[types.Bool])
|
|
t := typ.Elem()
|
|
for i := int64(0); i < typ.Len(); i++ {
|
|
lhs := newValue(b.CreateExtractValue(lhs.value, int(i), ""), t)
|
|
rhs := newValue(b.CreateExtractValue(rhs.value, int(i), ""), t)
|
|
value = fr.binaryOp(value, token.AND, fr.binaryOp(lhs, token.EQL, rhs))
|
|
}
|
|
return value
|
|
|
|
case *types.Slice:
|
|
// []T == nil or nil == []T
|
|
lhsptr := b.CreateExtractValue(lhs.value, 0, "")
|
|
rhsptr := b.CreateExtractValue(rhs.value, 0, "")
|
|
isnil := b.CreateICmp(llvm.IntEQ, lhsptr, rhsptr, "")
|
|
isnil = b.CreateZExt(isnil, llvm.Int8Type(), "")
|
|
return newValue(isnil, types.Typ[types.Bool])
|
|
|
|
case *types.Signature:
|
|
// func == nil or nil == func
|
|
isnil := b.CreateICmp(llvm.IntEQ, lhs.value, rhs.value, "")
|
|
isnil = b.CreateZExt(isnil, llvm.Int8Type(), "")
|
|
return newValue(isnil, types.Typ[types.Bool])
|
|
|
|
case *types.Interface:
|
|
return fr.compareInterfaces(lhs, rhs)
|
|
}
|
|
|
|
// Strings.
|
|
if isString(lhs.typ) {
|
|
if isString(rhs.typ) {
|
|
switch op {
|
|
case token.ADD:
|
|
return fr.concatenateStrings(lhs, rhs)
|
|
case token.EQL, token.LSS, token.GTR, token.LEQ, token.GEQ:
|
|
return fr.compareStrings(lhs, rhs, op)
|
|
default:
|
|
panic(fmt.Sprint("Unimplemented operator: ", op))
|
|
}
|
|
}
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// Complex numbers.
|
|
if isComplex(lhs.typ) {
|
|
// XXX Should we represent complex numbers as vectors?
|
|
lhsval := lhs.value
|
|
rhsval := rhs.value
|
|
a_ := b.CreateExtractValue(lhsval, 0, "")
|
|
b_ := b.CreateExtractValue(lhsval, 1, "")
|
|
c_ := b.CreateExtractValue(rhsval, 0, "")
|
|
d_ := b.CreateExtractValue(rhsval, 1, "")
|
|
switch op {
|
|
case token.QUO:
|
|
// (a+bi)/(c+di) = (ac+bd)/(c**2+d**2) + (bc-ad)/(c**2+d**2)i
|
|
ac := b.CreateFMul(a_, c_, "")
|
|
bd := b.CreateFMul(b_, d_, "")
|
|
bc := b.CreateFMul(b_, c_, "")
|
|
ad := b.CreateFMul(a_, d_, "")
|
|
cpow2 := b.CreateFMul(c_, c_, "")
|
|
dpow2 := b.CreateFMul(d_, d_, "")
|
|
denom := b.CreateFAdd(cpow2, dpow2, "")
|
|
realnumer := b.CreateFAdd(ac, bd, "")
|
|
imagnumer := b.CreateFSub(bc, ad, "")
|
|
real_ := b.CreateFDiv(realnumer, denom, "")
|
|
imag_ := b.CreateFDiv(imagnumer, denom, "")
|
|
lhsval = b.CreateInsertValue(lhsval, real_, 0, "")
|
|
result = b.CreateInsertValue(lhsval, imag_, 1, "")
|
|
case token.MUL:
|
|
// (a+bi)(c+di) = (ac-bd)+(bc+ad)i
|
|
ac := b.CreateFMul(a_, c_, "")
|
|
bd := b.CreateFMul(b_, d_, "")
|
|
bc := b.CreateFMul(b_, c_, "")
|
|
ad := b.CreateFMul(a_, d_, "")
|
|
real_ := b.CreateFSub(ac, bd, "")
|
|
imag_ := b.CreateFAdd(bc, ad, "")
|
|
lhsval = b.CreateInsertValue(lhsval, real_, 0, "")
|
|
result = b.CreateInsertValue(lhsval, imag_, 1, "")
|
|
case token.ADD:
|
|
real_ := b.CreateFAdd(a_, c_, "")
|
|
imag_ := b.CreateFAdd(b_, d_, "")
|
|
lhsval = b.CreateInsertValue(lhsval, real_, 0, "")
|
|
result = b.CreateInsertValue(lhsval, imag_, 1, "")
|
|
case token.SUB:
|
|
real_ := b.CreateFSub(a_, c_, "")
|
|
imag_ := b.CreateFSub(b_, d_, "")
|
|
lhsval = b.CreateInsertValue(lhsval, real_, 0, "")
|
|
result = b.CreateInsertValue(lhsval, imag_, 1, "")
|
|
case token.EQL:
|
|
realeq := b.CreateFCmp(llvm.FloatOEQ, a_, c_, "")
|
|
imageq := b.CreateFCmp(llvm.FloatOEQ, b_, d_, "")
|
|
result = b.CreateAnd(realeq, imageq, "")
|
|
result = b.CreateZExt(result, llvm.Int8Type(), "")
|
|
return newValue(result, types.Typ[types.Bool])
|
|
default:
|
|
panic(fmt.Errorf("unhandled operator: %v", op))
|
|
}
|
|
return newValue(result, lhs.typ)
|
|
}
|
|
|
|
// Floats and integers.
|
|
// TODO determine the NaN rules.
|
|
|
|
switch op {
|
|
case token.MUL:
|
|
if isFloat(lhs.typ) {
|
|
result = b.CreateFMul(lhs.value, rhs.value, "")
|
|
} else {
|
|
result = b.CreateMul(lhs.value, rhs.value, "")
|
|
}
|
|
return newValue(result, lhs.typ)
|
|
case token.QUO:
|
|
switch {
|
|
case isFloat(lhs.typ):
|
|
result = b.CreateFDiv(lhs.value, rhs.value, "")
|
|
case !isUnsigned(lhs.typ):
|
|
result = b.CreateSDiv(lhs.value, rhs.value, "")
|
|
default:
|
|
result = b.CreateUDiv(lhs.value, rhs.value, "")
|
|
}
|
|
return newValue(result, lhs.typ)
|
|
case token.REM:
|
|
switch {
|
|
case isFloat(lhs.typ):
|
|
result = b.CreateFRem(lhs.value, rhs.value, "")
|
|
case !isUnsigned(lhs.typ):
|
|
result = b.CreateSRem(lhs.value, rhs.value, "")
|
|
default:
|
|
result = b.CreateURem(lhs.value, rhs.value, "")
|
|
}
|
|
return newValue(result, lhs.typ)
|
|
case token.ADD:
|
|
if isFloat(lhs.typ) {
|
|
result = b.CreateFAdd(lhs.value, rhs.value, "")
|
|
} else {
|
|
result = b.CreateAdd(lhs.value, rhs.value, "")
|
|
}
|
|
return newValue(result, lhs.typ)
|
|
case token.SUB:
|
|
if isFloat(lhs.typ) {
|
|
result = b.CreateFSub(lhs.value, rhs.value, "")
|
|
} else {
|
|
result = b.CreateSub(lhs.value, rhs.value, "")
|
|
}
|
|
return newValue(result, lhs.typ)
|
|
case token.SHL, token.SHR:
|
|
return fr.shift(lhs, rhs, op)
|
|
case token.EQL:
|
|
if isFloat(lhs.typ) {
|
|
result = b.CreateFCmp(llvm.FloatOEQ, lhs.value, rhs.value, "")
|
|
} else {
|
|
result = b.CreateICmp(llvm.IntEQ, lhs.value, rhs.value, "")
|
|
}
|
|
result = b.CreateZExt(result, llvm.Int8Type(), "")
|
|
return newValue(result, types.Typ[types.Bool])
|
|
case token.LSS:
|
|
switch {
|
|
case isFloat(lhs.typ):
|
|
result = b.CreateFCmp(llvm.FloatOLT, lhs.value, rhs.value, "")
|
|
case !isUnsigned(lhs.typ):
|
|
result = b.CreateICmp(llvm.IntSLT, lhs.value, rhs.value, "")
|
|
default:
|
|
result = b.CreateICmp(llvm.IntULT, lhs.value, rhs.value, "")
|
|
}
|
|
result = b.CreateZExt(result, llvm.Int8Type(), "")
|
|
return newValue(result, types.Typ[types.Bool])
|
|
case token.LEQ:
|
|
switch {
|
|
case isFloat(lhs.typ):
|
|
result = b.CreateFCmp(llvm.FloatOLE, lhs.value, rhs.value, "")
|
|
case !isUnsigned(lhs.typ):
|
|
result = b.CreateICmp(llvm.IntSLE, lhs.value, rhs.value, "")
|
|
default:
|
|
result = b.CreateICmp(llvm.IntULE, lhs.value, rhs.value, "")
|
|
}
|
|
result = b.CreateZExt(result, llvm.Int8Type(), "")
|
|
return newValue(result, types.Typ[types.Bool])
|
|
case token.GTR:
|
|
switch {
|
|
case isFloat(lhs.typ):
|
|
result = b.CreateFCmp(llvm.FloatOGT, lhs.value, rhs.value, "")
|
|
case !isUnsigned(lhs.typ):
|
|
result = b.CreateICmp(llvm.IntSGT, lhs.value, rhs.value, "")
|
|
default:
|
|
result = b.CreateICmp(llvm.IntUGT, lhs.value, rhs.value, "")
|
|
}
|
|
result = b.CreateZExt(result, llvm.Int8Type(), "")
|
|
return newValue(result, types.Typ[types.Bool])
|
|
case token.GEQ:
|
|
switch {
|
|
case isFloat(lhs.typ):
|
|
result = b.CreateFCmp(llvm.FloatOGE, lhs.value, rhs.value, "")
|
|
case !isUnsigned(lhs.typ):
|
|
result = b.CreateICmp(llvm.IntSGE, lhs.value, rhs.value, "")
|
|
default:
|
|
result = b.CreateICmp(llvm.IntUGE, lhs.value, rhs.value, "")
|
|
}
|
|
result = b.CreateZExt(result, llvm.Int8Type(), "")
|
|
return newValue(result, types.Typ[types.Bool])
|
|
case token.AND: // a & b
|
|
result = b.CreateAnd(lhs.value, rhs.value, "")
|
|
return newValue(result, lhs.typ)
|
|
case token.AND_NOT: // a &^ b
|
|
rhsval := rhs.value
|
|
rhsval = b.CreateXor(rhsval, llvm.ConstAllOnes(rhsval.Type()), "")
|
|
result = b.CreateAnd(lhs.value, rhsval, "")
|
|
return newValue(result, lhs.typ)
|
|
case token.OR: // a | b
|
|
result = b.CreateOr(lhs.value, rhs.value, "")
|
|
return newValue(result, lhs.typ)
|
|
case token.XOR: // a ^ b
|
|
result = b.CreateXor(lhs.value, rhs.value, "")
|
|
return newValue(result, lhs.typ)
|
|
default:
|
|
panic(fmt.Sprint("Unimplemented operator: ", op))
|
|
}
|
|
panic("unreachable")
|
|
}
|
|
|
|
func (fr *frame) shift(lhs *govalue, rhs *govalue, op token.Token) *govalue {
|
|
rhs = fr.convert(rhs, lhs.Type())
|
|
lhsval := lhs.value
|
|
bits := rhs.value
|
|
unsigned := isUnsigned(lhs.Type())
|
|
// Shifting >= width of lhs yields undefined behaviour, so we must select.
|
|
max := llvm.ConstInt(bits.Type(), uint64(lhsval.Type().IntTypeWidth()-1), false)
|
|
var result llvm.Value
|
|
lessEqualWidth := fr.builder.CreateICmp(llvm.IntULE, bits, max, "")
|
|
if !unsigned && op == token.SHR {
|
|
bits := fr.builder.CreateSelect(lessEqualWidth, bits, max, "")
|
|
result = fr.builder.CreateAShr(lhsval, bits, "")
|
|
} else {
|
|
if op == token.SHL {
|
|
result = fr.builder.CreateShl(lhsval, bits, "")
|
|
} else {
|
|
result = fr.builder.CreateLShr(lhsval, bits, "")
|
|
}
|
|
zero := llvm.ConstNull(lhsval.Type())
|
|
result = fr.builder.CreateSelect(lessEqualWidth, result, zero, "")
|
|
}
|
|
return newValue(result, lhs.typ)
|
|
}
|
|
|
|
func (fr *frame) unaryOp(v *govalue, op token.Token) *govalue {
|
|
switch op {
|
|
case token.SUB:
|
|
var value llvm.Value
|
|
if isComplex(v.typ) {
|
|
realv := fr.builder.CreateExtractValue(v.value, 0, "")
|
|
imagv := fr.builder.CreateExtractValue(v.value, 1, "")
|
|
negzero := llvm.ConstFloatFromString(realv.Type(), "-0")
|
|
realv = fr.builder.CreateFSub(negzero, realv, "")
|
|
imagv = fr.builder.CreateFSub(negzero, imagv, "")
|
|
value = llvm.Undef(v.value.Type())
|
|
value = fr.builder.CreateInsertValue(value, realv, 0, "")
|
|
value = fr.builder.CreateInsertValue(value, imagv, 1, "")
|
|
} else if isFloat(v.typ) {
|
|
negzero := llvm.ConstFloatFromString(fr.types.ToLLVM(v.Type()), "-0")
|
|
value = fr.builder.CreateFSub(negzero, v.value, "")
|
|
} else {
|
|
value = fr.builder.CreateNeg(v.value, "")
|
|
}
|
|
return newValue(value, v.typ)
|
|
case token.ADD:
|
|
return v // No-op
|
|
case token.NOT:
|
|
value := fr.builder.CreateXor(v.value, boolLLVMValue(true), "")
|
|
return newValue(value, v.typ)
|
|
case token.XOR:
|
|
lhs := v.value
|
|
rhs := llvm.ConstAllOnes(lhs.Type())
|
|
value := fr.builder.CreateXor(lhs, rhs, "")
|
|
return newValue(value, v.typ)
|
|
default:
|
|
panic(fmt.Sprintf("Unhandled operator: %s", op))
|
|
}
|
|
}
|
|
|
|
func (fr *frame) convert(v *govalue, dsttyp types.Type) *govalue {
|
|
b := fr.builder
|
|
|
|
// If it's a stack allocated value, we'll want to compare the
|
|
// value type, not the pointer type.
|
|
srctyp := v.typ
|
|
|
|
// Get the underlying type, if any.
|
|
origdsttyp := dsttyp
|
|
dsttyp = dsttyp.Underlying()
|
|
srctyp = srctyp.Underlying()
|
|
|
|
// Identical (underlying) types? Just swap in the destination type.
|
|
if types.Identical(srctyp, dsttyp) {
|
|
return newValue(v.value, origdsttyp)
|
|
}
|
|
|
|
// Both pointer types with identical underlying types? Same as above.
|
|
if srctyp, ok := srctyp.(*types.Pointer); ok {
|
|
if dsttyp, ok := dsttyp.(*types.Pointer); ok {
|
|
srctyp := srctyp.Elem().Underlying()
|
|
dsttyp := dsttyp.Elem().Underlying()
|
|
if types.Identical(srctyp, dsttyp) {
|
|
return newValue(v.value, origdsttyp)
|
|
}
|
|
}
|
|
}
|
|
|
|
// string ->
|
|
if isString(srctyp) {
|
|
// (untyped) string -> string
|
|
// XXX should untyped strings be able to escape go/types?
|
|
if isString(dsttyp) {
|
|
return newValue(v.value, origdsttyp)
|
|
}
|
|
|
|
// string -> []byte
|
|
if isSlice(dsttyp, types.Byte) {
|
|
sliceValue := fr.runtime.stringToByteArray.callOnly(fr, v.value)[0]
|
|
return newValue(sliceValue, origdsttyp)
|
|
}
|
|
|
|
// string -> []rune
|
|
if isSlice(dsttyp, types.Rune) {
|
|
return fr.stringToRuneSlice(v)
|
|
}
|
|
}
|
|
|
|
// []byte -> string
|
|
if isSlice(srctyp, types.Byte) && isString(dsttyp) {
|
|
data := fr.builder.CreateExtractValue(v.value, 0, "")
|
|
len := fr.builder.CreateExtractValue(v.value, 1, "")
|
|
stringValue := fr.runtime.byteArrayToString.callOnly(fr, data, len)[0]
|
|
return newValue(stringValue, dsttyp)
|
|
}
|
|
|
|
// []rune -> string
|
|
if isSlice(srctyp, types.Rune) && isString(dsttyp) {
|
|
return fr.runeSliceToString(v)
|
|
}
|
|
|
|
// rune -> string
|
|
if isString(dsttyp) && isInteger(srctyp) {
|
|
return fr.runeToString(v)
|
|
}
|
|
|
|
// Unsafe pointer conversions.
|
|
llvm_type := fr.types.ToLLVM(dsttyp)
|
|
if dsttyp == types.Typ[types.UnsafePointer] { // X -> unsafe.Pointer
|
|
if _, isptr := srctyp.(*types.Pointer); isptr {
|
|
return newValue(v.value, origdsttyp)
|
|
} else if srctyp == types.Typ[types.Uintptr] {
|
|
value := b.CreateIntToPtr(v.value, llvm_type, "")
|
|
return newValue(value, origdsttyp)
|
|
}
|
|
} else if srctyp == types.Typ[types.UnsafePointer] { // unsafe.Pointer -> X
|
|
if _, isptr := dsttyp.(*types.Pointer); isptr {
|
|
return newValue(v.value, origdsttyp)
|
|
} else if dsttyp == types.Typ[types.Uintptr] {
|
|
value := b.CreatePtrToInt(v.value, llvm_type, "")
|
|
return newValue(value, origdsttyp)
|
|
}
|
|
}
|
|
|
|
lv := v.value
|
|
srcType := lv.Type()
|
|
switch srcType.TypeKind() {
|
|
case llvm.IntegerTypeKind:
|
|
switch llvm_type.TypeKind() {
|
|
case llvm.IntegerTypeKind:
|
|
srcBits := srcType.IntTypeWidth()
|
|
dstBits := llvm_type.IntTypeWidth()
|
|
delta := srcBits - dstBits
|
|
switch {
|
|
case delta < 0:
|
|
if !isUnsigned(srctyp) {
|
|
lv = b.CreateSExt(lv, llvm_type, "")
|
|
} else {
|
|
lv = b.CreateZExt(lv, llvm_type, "")
|
|
}
|
|
case delta > 0:
|
|
lv = b.CreateTrunc(lv, llvm_type, "")
|
|
}
|
|
return newValue(lv, origdsttyp)
|
|
case llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
|
if !isUnsigned(v.Type()) {
|
|
lv = b.CreateSIToFP(lv, llvm_type, "")
|
|
} else {
|
|
lv = b.CreateUIToFP(lv, llvm_type, "")
|
|
}
|
|
return newValue(lv, origdsttyp)
|
|
}
|
|
case llvm.DoubleTypeKind:
|
|
switch llvm_type.TypeKind() {
|
|
case llvm.FloatTypeKind:
|
|
lv = b.CreateFPTrunc(lv, llvm_type, "")
|
|
return newValue(lv, origdsttyp)
|
|
case llvm.IntegerTypeKind:
|
|
if !isUnsigned(dsttyp) {
|
|
lv = b.CreateFPToSI(lv, llvm_type, "")
|
|
} else {
|
|
lv = b.CreateFPToUI(lv, llvm_type, "")
|
|
}
|
|
return newValue(lv, origdsttyp)
|
|
}
|
|
case llvm.FloatTypeKind:
|
|
switch llvm_type.TypeKind() {
|
|
case llvm.DoubleTypeKind:
|
|
lv = b.CreateFPExt(lv, llvm_type, "")
|
|
return newValue(lv, origdsttyp)
|
|
case llvm.IntegerTypeKind:
|
|
if !isUnsigned(dsttyp) {
|
|
lv = b.CreateFPToSI(lv, llvm_type, "")
|
|
} else {
|
|
lv = b.CreateFPToUI(lv, llvm_type, "")
|
|
}
|
|
return newValue(lv, origdsttyp)
|
|
}
|
|
}
|
|
|
|
// Complex -> complex. Complexes are only convertible to other
|
|
// complexes, contant conversions aside. So we can just check the
|
|
// source type here; given that the types are not identical
|
|
// (checked above), we can assume the destination type is the alternate
|
|
// complex type.
|
|
if isComplex(srctyp) {
|
|
var fpcast func(llvm.Builder, llvm.Value, llvm.Type, string) llvm.Value
|
|
var fptype llvm.Type
|
|
if srctyp == types.Typ[types.Complex64] {
|
|
fpcast = (llvm.Builder).CreateFPExt
|
|
fptype = llvm.DoubleType()
|
|
} else {
|
|
fpcast = (llvm.Builder).CreateFPTrunc
|
|
fptype = llvm.FloatType()
|
|
}
|
|
if fpcast != nil {
|
|
realv := b.CreateExtractValue(lv, 0, "")
|
|
imagv := b.CreateExtractValue(lv, 1, "")
|
|
realv = fpcast(b, realv, fptype, "")
|
|
imagv = fpcast(b, imagv, fptype, "")
|
|
lv = llvm.Undef(fr.types.ToLLVM(dsttyp))
|
|
lv = b.CreateInsertValue(lv, realv, 0, "")
|
|
lv = b.CreateInsertValue(lv, imagv, 1, "")
|
|
return newValue(lv, origdsttyp)
|
|
}
|
|
}
|
|
panic(fmt.Sprintf("unimplemented conversion: %s (%s) -> %s", v.typ, lv.Type(), origdsttyp))
|
|
}
|
|
|
|
// extractRealValue extracts the real component of a complex number.
|
|
func (fr *frame) extractRealValue(v *govalue) *govalue {
|
|
component := fr.builder.CreateExtractValue(v.value, 0, "")
|
|
if component.Type().TypeKind() == llvm.FloatTypeKind {
|
|
return newValue(component, types.Typ[types.Float32])
|
|
}
|
|
return newValue(component, types.Typ[types.Float64])
|
|
}
|
|
|
|
// extractRealValue extracts the imaginary component of a complex number.
|
|
func (fr *frame) extractImagValue(v *govalue) *govalue {
|
|
component := fr.builder.CreateExtractValue(v.value, 1, "")
|
|
if component.Type().TypeKind() == llvm.FloatTypeKind {
|
|
return newValue(component, types.Typ[types.Float32])
|
|
}
|
|
return newValue(component, types.Typ[types.Float64])
|
|
}
|
|
|
|
func boolLLVMValue(v bool) (lv llvm.Value) {
|
|
if v {
|
|
return llvm.ConstInt(llvm.Int8Type(), 1, false)
|
|
}
|
|
return llvm.ConstNull(llvm.Int8Type())
|
|
}
|