// NewCharArray returns a character array constant based on the given array type // and string. func NewCharArray(typ types.Type, s string) (*Array, error) { // Verify array type. v := new(Array) var ok bool v.typ, ok = typ.(*types.Array) if !ok { return nil, fmt.Errorf("invalid type %q for array constant", typ) } var err error s, err = unquote(s) if err != nil { return nil, errutil.Err(err) } // Verify array element types. if len(s) != v.typ.Len() { return nil, fmt.Errorf("incorrect number of elements in character array constant; expected %d, got %d", v.typ.Len(), len(s)) } elemType := v.typ.Elem() if !types.Equal(elemType, types.I8) { return nil, fmt.Errorf("invalid character array element type; expected %q, got %q", types.I8, elemType) } for i := 0; i < len(s); i++ { elem, err := NewInt(elemType, strconv.Itoa(int(s[i]))) if err != nil { return nil, errutil.Err(err) } v.elems = append(v.elems, elem) } return v, nil }
// NewGetElementPtrInst returns a new getelementptr instruction based on the // given element type, address and element indices. func NewGetElementPtrInst(elem, srcAddrType, srcAddr, elemIndices interface{}) (*instruction.GetElementPtr, error) { if elem, ok := elem.(types.Type); ok { srcAddr, err := NewValue(srcAddrType, srcAddr) if err != nil { return nil, errutil.Err(err) } var indices []value.Value switch elemIndices := elemIndices.(type) { case []value.Value: indices = elemIndices case nil: default: panic(fmt.Sprintf("support for element indices type %T not yet implemented", elemIndices)) } // Validate that elem is of identical type as the element type of srcAddr. srcAddrType, ok := srcAddr.Type().(*types.Pointer) if !ok { return nil, errutil.Newf("invalid source address pointer type; expected *types.Pointer, got %T", srcAddr.Type()) } if !types.Equal(elem, srcAddrType.Elem()) { return nil, errutil.Newf("type mismatch between element type (%v) and source address element type (%v)", elem, srcAddrType.Elem()) } return instruction.NewGetElementPtr(srcAddr, indices) } return nil, errutil.Newf("invalid operand type; expected types.Type, got %T", elem) }
// convert converts the given value to the specified type, emitting code to f. // No conversion is made, if v is already of the correct type. func (m *Module) convert(f *Function, v value.Value, to irtypes.Type) value.Value { // Early return if v is already of the correct type. from := v.Type() if irtypes.Equal(from, to) { return v } fromType, ok := from.(*irtypes.Int) if !ok { panic(fmt.Sprintf("support for converting from type %T not yet implemented", from)) } toType, ok := to.(*irtypes.Int) if !ok { panic(fmt.Sprintf("support for converting to type %T not yet implemented", to)) } // Convert constant values. if v, ok := v.(constant.Constant); ok { switch v := v.(type) { case *constant.Int: v, err := constant.NewInt(toType, v.ValueString()) if err != nil { panic(fmt.Sprintf("unable to create integer constant; %v", err)) } return v default: panic(fmt.Sprintf("support for converting type %T not yet implemented", v)) } } // TODO: Add proper support for converting signed and unsigned values, using // sext and zext, respectively. // Convert unsigned values. if irtypes.IsBool(fromType) { // Zero extend boolean values. zextInst, err := instruction.NewZExt(v, toType) if err != nil { panic(fmt.Sprintf("unable to create sext instruction; %v", err)) } return f.emitInst(zextInst) } // Convert signed values. if toType.Size() > fromType.Size() { // Sign extend. sextInst, err := instruction.NewSExt(v, toType) if err != nil { panic(fmt.Sprintf("unable to create sext instruction; %v", err)) } return f.emitInst(sextInst) } // Truncate. truncInst, err := instruction.NewTrunc(v, toType) if err != nil { panic(fmt.Sprintf("unable to create trunc instruction; %v", err)) } return f.emitInst(truncInst) }
// NewBr returns a new conditional branch instruction based on the given // branching condition, and the true and false target branches. func NewBr(cond value.Value, trueBranch, falseBranch value.NamedValue) (*Br, error) { // TODO: Validate that trueBranch and falseBranch are of type *ir.BasicBlock. // Better yet, chance the signature of NewBr to enforce this. Another // approach, is to simply check that the type of trueBranch and falseBranch // are both "label". if !types.Equal(cond.Type(), types.I1) { return nil, errutil.Newf("conditional type mismatch; expected i1, got %v", cond.Type()) } return &Br{cond: cond, trueBranch: trueBranch, falseBranch: falseBranch}, nil }
// NewSelect returns a new select instruction based on the given selection // condition, and operands. // // Pre-condition: cond is of boolean or boolean vector type. x and y are of // identical types. func NewSelect(cond, x, y value.Value) (*Select, error) { // Validate that cond is of boolean or boolean vector type. if !types.IsBools(cond.Type()) { return nil, errutil.Newf("invalid selection condition type; expected boolean or boolean vector, got %v", cond.Type()) } // Validate that x and y are of identical types. if !types.Equal(x.Type(), y.Type()) { return nil, errutil.Newf("type mismatch between x (%v) and y (%v)", x.Type(), y.Type()) } return &Select{cond: cond, x: x, y: y}, nil }
// indexExpr lowers the given index expression to LLVM IR, emitting code to f. func (m *Module) indexExpr(f *Function, n *ast.IndexExpr) value.Value { index := m.expr(f, n.Index) // Extend the index to a 64-bit integer. if !irtypes.Equal(index.Type(), irtypes.I64) { index = m.convert(f, index, irtypes.I64) } typ := m.typeOf(n.Name) array := m.valueFromIdent(f, n.Name) // Dereference pointer pointer. elem := typ addr := array zero := constZero(irtypes.I64) indices := []value.Value{zero, index} if typ, ok := typ.(*irtypes.Pointer); ok { elem = typ.Elem() // Emit load instruction. // TODO: Validate typ against array.Elem(). loadInst, err := instruction.NewLoad(array) if err != nil { panic(fmt.Sprintf("unable to create load instruction; %v", err)) } addr = f.emitInst(loadInst) indices = []value.Value{index} } // Emit getelementptr instruction. if m.isGlobal(n.Name) { var is []constant.Constant for _, index := range indices { i, ok := index.(constant.Constant) if !ok { break } is = append(is, i) } if len(is) == len(indices) { // In accordance with Clang, emit getelementptr constant expressions // for global variables. gepExpr, err := constant.NewGetElementPtr(elem, addr, is) if err != nil { panic(fmt.Sprintf("unable to create getelementptr expression; %v", err)) } return gepExpr } } // TODO: Validate elem against array.Elem(). gepInst, err := instruction.NewGetElementPtr(addr, indices) if err != nil { panic(fmt.Sprintf("unable to create getelementptr instruction; %v", err)) } return f.emitInst(gepInst) }
// NewStore returns a new store instruction based on the given source value and // destination address. // // Pre-condition: // 1. dstAddr is of pointer type // 2. src is of identical type as the element type of dstAddr func NewStore(src, dstAddr value.Value) (*Store, error) { // Validate that dstAddr is of pointer type. dstAddrType, ok := dstAddr.Type().(*types.Pointer) if !ok { return nil, errutil.Newf("invalid destination address pointer type; expected *types.Pointer, got %T", dstAddr.Type()) } // Validate that src is of identical type as the element type of dstAddr. if !types.Equal(src.Type(), dstAddrType.Elem()) { return nil, errutil.Newf("type mismatch between source value (%v) and destination address element type (%v)", src.Type(), dstAddrType.Elem()) } return &Store{src: src, dstAddr: dstAddr}, nil }
// NewICmp returns a new icmp instruction based on the given condition and // operands. // // Pre-condition: x and y are of identical types. x and y are of integer, // integer vector, pointer or pointer vector type. func NewICmp(cond ICond, x, y value.Value) (*ICmp, error) { // Validate that x and y are of identical types. if !types.Equal(x.Type(), y.Type()) { return nil, errutil.Newf("type mismatch between x (%v) and y (%v)", x.Type(), y.Type()) } // Validate that x and y are of integer, integer vector, pointer or pointer // vector type. if !types.IsInts(x.Type()) && !types.IsPointers(x.Type()) { return nil, errutil.Newf("invalid x operand type; expected integer, integer vector, pointer or pointer vector, got %v", x.Type()) } if !types.IsInts(y.Type()) && !types.IsPointers(y.Type()) { return nil, errutil.Newf("invalid y operand type; expected integer, integer vector, pointer or pointer vector, got %v", y.Type()) } return &ICmp{cond: cond, x: x, y: y}, nil }
// NewPHI returns a new phi instruction based on the given incoming values. // // Pre-condition: incs is non-empty and each incoming value is of identical // type. func NewPHI(incs []*Incoming) (*PHI, error) { // Validate that incs is non-empty. if len(incs) < 1 { return nil, errutil.Newf("invalid number of incoming values; expected > 0, got %d", len(incs)) } // Validate that each incoming value is of identical type. a := incs[0].val.Type() for i := 1; i < len(incs); i++ { b := incs[i].val.Type() if !types.Equal(a, b) { return nil, errutil.Newf("type mismatch between incoming value 0 (%v) and %d (%v)", a, i, b) } } return &PHI{incs: incs}, nil }
// NewLoadInst returns a new load instruction based on the given type and // address. func NewLoadInst(typ, srcAddrType, srcAddr interface{}) (*instruction.Load, error) { if typ, ok := typ.(types.Type); ok { srcAddr, err := NewValue(srcAddrType, srcAddr) if err != nil { return nil, errutil.Err(err) } srcAddrType, ok := srcAddr.Type().(*types.Pointer) if !ok { return nil, errutil.Newf("invalid source address pointer type; expected *types.Pointer, got %T", srcAddr.Type()) } if !types.Equal(typ, srcAddrType.Elem()) { return nil, errutil.Newf("type mismatch between element type (%v) and source address element type (%v)", typ, srcAddrType.Elem()) } return instruction.NewLoad(srcAddr) } return nil, errutil.Newf("invalid operand type; expected types.Type, got %T", typ) }
// NewGetElementPtr returns a new getelementptr expression based on the given // element type, address and element indices. // // Preconditions: // * elem is of the same type as addr.Type().Elem(). // * addr is of pointer type. // * indices used to index structure fields are integer constants. func NewGetElementPtr(elem types.Type, addr value.Value, indices []Constant) (*GetElementPtr, error) { // Sanity checks. addrType, ok := addr.Type().(*types.Pointer) if !ok { return nil, errutil.Newf("invalid pointer type; expected *types.Pointer, got %T", addr.Type()) } if !types.Equal(elem, addrType.Elem()) { return nil, errutil.Newf("type mismatch between %v and %v", elem, addrType.Elem()) } e := addrType.Elem() for i, index := range indices { if i == 0 { // Ignore checking the 0th index as it simply follows the pointer of // addr. // // ref: http://llvm.org/docs/GetElementPtr.html#why-is-the-extra-0-index-required continue } switch ee := e.(type) { case *types.Pointer: // ref: http://llvm.org/docs/GetElementPtr.html#what-is-dereferenced-by-gep return nil, errutil.Newf(`unable to index into element of pointer type; for more information, see http://llvm.org/docs/GetElementPtr.html#what-is-dereferenced-by-gep`) case *types.Array: e = ee.Elem() case *types.Struct: idx, ok := index.(*Int) if !ok { return nil, errutil.Newf("invalid index type for structure element; expected *constant.Int, got %T", index) } e = ee.Fields()[idx.Value().Int64()] default: panic(fmt.Sprintf("constant.NewGetElementPtr: support for indexing element type %T not yet implemented", e)) } } typ, err := types.NewPointer(e) if err != nil { return nil, errutil.Err(err) } return &GetElementPtr{typ: typ, elem: elem, addr: addr, indices: indices}, nil }
// NewFAdd returns a new fadd instruction based on the given operands. func NewFAdd(x, y value.Value) (*FAdd, error) { if !types.Equal(x.Type(), y.Type()) { return nil, errutil.Newf("type mismatch between x (%v) and y (%v)", x.Type(), y.Type()) } return &FAdd{x: x, y: y}, nil }
func TestEqual(t *testing.T) { golden := []struct { want bool a, b types.Type }{ {want: true, a: voidTyp, b: voidTyp}, {want: true, a: i1Typ, b: i1Typ}, {want: true, a: i8Typ, b: i8Typ}, {want: true, a: i32Typ, b: i32Typ}, {want: true, a: f16Typ, b: f16Typ}, {want: true, a: f32Typ, b: f32Typ}, {want: true, a: f64Typ, b: f64Typ}, {want: true, a: f128Typ, b: f128Typ}, {want: true, a: f80_x86Typ, b: f80_x86Typ}, {want: true, a: f128_ppcTyp, b: f128_ppcTyp}, {want: true, a: mmxTyp, b: mmxTyp}, {want: true, a: labelTyp, b: labelTyp}, {want: true, a: metadataTyp, b: metadataTyp}, {want: true, a: voidFuncTyp, b: voidFuncTyp}, {want: true, a: i32FuncTyp, b: i32FuncTyp}, {want: true, a: voidFunci32Typ, b: voidFunci32Typ}, {want: true, a: voidFuncf32Typ, b: voidFuncf32Typ}, {want: true, a: voidFunci32EllipsisTyp, b: voidFunci32EllipsisTyp}, {want: true, a: funcTyp, b: funcTyp}, {want: true, a: i8PtrTyp, b: i8PtrTyp}, {want: true, a: f16PtrTyp, b: f16PtrTyp}, {want: true, a: mmxPtrTyp, b: mmxPtrTyp}, {want: true, a: funcPtrTyp, b: funcPtrTyp}, {want: true, a: i8x1VecTyp, b: i8x1VecTyp}, {want: true, a: i32x2VecTyp, b: i32x2VecTyp}, {want: true, a: f16x3VecTyp, b: f16x3VecTyp}, {want: true, a: f32x4VecTyp, b: f32x4VecTyp}, {want: true, a: f64x5VecTyp, b: f64x5VecTyp}, {want: true, a: f128x6VecTyp, b: f128x6VecTyp}, {want: true, a: f80_x86x7VecTyp, b: f80_x86x7VecTyp}, {want: true, a: f128_ppcx8VecTyp, b: f128_ppcx8VecTyp}, {want: true, a: i8Ptrx9VecTyp, b: i8Ptrx9VecTyp}, {want: true, a: f16Ptrx10VecTyp, b: f16Ptrx10VecTyp}, {want: true, a: i8x1ArrTyp, b: i8x1ArrTyp}, {want: true, a: i32x2ArrTyp, b: i32x2ArrTyp}, {want: true, a: f16x3ArrTyp, b: f16x3ArrTyp}, {want: true, a: f32x4ArrTyp, b: f32x4ArrTyp}, {want: true, a: f64x5ArrTyp, b: f64x5ArrTyp}, {want: true, a: f128x6ArrTyp, b: f128x6ArrTyp}, {want: true, a: f80_x86x7ArrTyp, b: f80_x86x7ArrTyp}, {want: true, a: f128_ppcx8ArrTyp, b: f128_ppcx8ArrTyp}, {want: true, a: i8Ptrx9ArrTyp, b: i8Ptrx9ArrTyp}, {want: true, a: f16Ptrx10ArrTyp, b: f16Ptrx10ArrTyp}, {want: true, a: i32i8structTyp, b: i32i8structTyp}, {want: true, a: i32i32structTyp, b: i32i32structTyp}, {want: true, a: i32i8i8structTyp, b: i32i8i8structTyp}, {want: true, a: structTyp, b: structTyp}, {want: false, a: voidTyp, b: structTyp}, {want: false, a: i1Typ, b: voidTyp}, {want: false, a: i8Typ, b: i1Typ}, {want: false, a: i32Typ, b: i8Typ}, {want: false, a: f16Typ, b: i32Typ}, {want: false, a: f32Typ, b: f16Typ}, {want: false, a: f64Typ, b: f32Typ}, {want: false, a: f128Typ, b: f64Typ}, {want: false, a: f80_x86Typ, b: f128Typ}, {want: false, a: f128_ppcTyp, b: f80_x86Typ}, {want: false, a: mmxTyp, b: f128_ppcTyp}, {want: false, a: labelTyp, b: mmxTyp}, {want: false, a: metadataTyp, b: labelTyp}, {want: false, a: voidFuncTyp, b: i32FuncTyp}, {want: false, a: voidFuncTyp, b: voidFunci32Typ}, {want: false, a: voidFunci32Typ, b: voidFuncf32Typ}, {want: false, a: voidFunci32Typ, b: voidFunci32EllipsisTyp}, {want: false, a: funcTyp, b: metadataTyp}, {want: false, a: i8PtrTyp, b: funcTyp}, {want: false, a: f16PtrTyp, b: i8PtrTyp}, {want: false, a: mmxPtrTyp, b: f16PtrTyp}, {want: false, a: funcPtrTyp, b: mmxPtrTyp}, {want: false, a: i8x1VecTyp, b: funcPtrTyp}, {want: false, a: i32x2VecTyp, b: i8x1VecTyp}, {want: false, a: f16x3VecTyp, b: i32x2VecTyp}, {want: false, a: f32x4VecTyp, b: f16x3VecTyp}, {want: false, a: f64x5VecTyp, b: f32x4VecTyp}, {want: false, a: f128x6VecTyp, b: f64x5VecTyp}, {want: false, a: f80_x86x7VecTyp, b: f128x6VecTyp}, {want: false, a: f128_ppcx8VecTyp, b: f80_x86x7VecTyp}, {want: false, a: i8Ptrx9VecTyp, b: f128_ppcx8VecTyp}, {want: false, a: f16Ptrx10VecTyp, b: i8Ptrx9VecTyp}, {want: false, a: i8x1ArrTyp, b: f16Ptrx10VecTyp}, {want: false, a: i32x2ArrTyp, b: i8x1ArrTyp}, {want: false, a: f16x3ArrTyp, b: i32x2ArrTyp}, {want: false, a: f32x4ArrTyp, b: f16x3ArrTyp}, {want: false, a: f64x5ArrTyp, b: f32x4ArrTyp}, {want: false, a: f128x6ArrTyp, b: f64x5ArrTyp}, {want: false, a: f80_x86x7ArrTyp, b: f128x6ArrTyp}, {want: false, a: f128_ppcx8ArrTyp, b: f80_x86x7ArrTyp}, {want: false, a: i8Ptrx9ArrTyp, b: f128_ppcx8ArrTyp}, {want: false, a: f16Ptrx10ArrTyp, b: i8Ptrx9ArrTyp}, {want: false, a: i32i8structTyp, b: i32i8i8structTyp}, {want: false, a: i32i8structTyp, b: i32i32structTyp}, {want: false, a: structTyp, b: f16Ptrx10ArrTyp}, } for i, g := range golden { got := types.Equal(g.a, g.b) if got != g.want { t.Errorf("i=%d: expected %v, got %v", i, g.want, got) } } }