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) chanSelect(sel *ssa.Select) (index, recvOk *govalue, recvElems []*govalue) { n := uint64(len(sel.States)) if !sel.Blocking { // non-blocking means there's a default case n++ } size := llvm.ConstInt(llvm.Int32Type(), n, false) selectp := fr.runtime.newSelect.call(fr, size)[0] // Allocate stack for the values to send and receive. ptrs := make([]llvm.Value, len(sel.States)) for i, state := range sel.States { chantyp := state.Chan.Type().Underlying().(*types.Chan) elemtyp := fr.types.ToLLVM(chantyp.Elem()) if state.Dir == types.SendOnly { ptrs[i] = fr.allocaBuilder.CreateAlloca(elemtyp, "") fr.builder.CreateStore(fr.llvmvalue(state.Send), ptrs[i]) } else { // Only allocate stack space if the received value is used. used := chanSelectStateUsed(sel, len(recvElems)) if used { ptrs[i] = fr.allocaBuilder.CreateAlloca(elemtyp, "") } else { ptrs[i] = llvm.ConstNull(llvm.PointerType(llvm.Int8Type(), 0)) } recvElems = append(recvElems, newValue(ptrs[i], chantyp.Elem())) } } // Create select{send,recv2} calls. var receivedp llvm.Value if len(recvElems) > 0 { receivedp = fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(types.Typ[types.Bool]), "") } if !sel.Blocking { // If the default case is chosen, the index must be -1. fr.runtime.selectdefault.call(fr, selectp, llvm.ConstAllOnes(llvm.Int32Type())) } for i, state := range sel.States { ch := fr.llvmvalue(state.Chan) index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false) if state.Dir == types.SendOnly { fr.runtime.selectsend.call(fr, selectp, ch, ptrs[i], index) } else { fr.runtime.selectrecv2.call(fr, selectp, ch, ptrs[i], receivedp, index) } } // Fire off the select. index = newValue(fr.runtime.selectgo.call(fr, selectp)[0], types.Typ[types.Int]) if len(recvElems) > 0 { recvOk = newValue(fr.builder.CreateLoad(receivedp, ""), types.Typ[types.Bool]) for _, recvElem := range recvElems { recvElem.value = fr.builder.CreateLoad(recvElem.value, "") } } return index, recvOk, recvElems }
func (fr *frame) chanSelect(states []selectState, blocking bool) (index, recvOk *govalue, recvElems []*govalue) { n := uint64(len(states)) if !blocking { // non-blocking means there's a default case n++ } size := llvm.ConstInt(llvm.Int32Type(), n, false) selectp := fr.runtime.newSelect.call(fr, size)[0] // Allocate stack for the values to send and receive. // // TODO(axw) check if received elements have any users, and // elide stack allocation if not (pass nil to recv2 instead.) ptrs := make([]llvm.Value, len(states)) for i, state := range states { chantyp := state.Chan.Type().Underlying().(*types.Chan) elemtyp := fr.types.ToLLVM(chantyp.Elem()) ptrs[i] = fr.allocaBuilder.CreateAlloca(elemtyp, "") if state.Dir == types.SendOnly { fr.builder.CreateStore(state.Send.value, ptrs[i]) } else { recvElems = append(recvElems, newValue(ptrs[i], chantyp.Elem())) } } // Create select{send,recv2} calls. var receivedp llvm.Value if len(recvElems) > 0 { receivedp = fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(types.Typ[types.Bool]), "") } if !blocking { // If the default case is chosen, the index must be -1. fr.runtime.selectdefault.call(fr, selectp, llvm.ConstAllOnes(llvm.Int32Type())) } for i, state := range states { ch := state.Chan.value index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false) if state.Dir == types.SendOnly { fr.runtime.selectsend.call(fr, selectp, ch, ptrs[i], index) } else { fr.runtime.selectrecv2.call(fr, selectp, ch, ptrs[i], receivedp, index) } } // Fire off the select. index = newValue(fr.runtime.selectgo.call(fr, selectp)[0], types.Typ[types.Int]) if len(recvElems) > 0 { recvOk = newValue(fr.builder.CreateLoad(receivedp, ""), types.Typ[types.Bool]) for _, recvElem := range recvElems { recvElem.value = fr.builder.CreateLoad(recvElem.value, "") } } return index, recvOk, recvElems }
func (fr *frame) slice(x llvm.Value, xtyp types.Type, low, high, max llvm.Value) llvm.Value { if !low.IsNil() { low = fr.createZExtOrTrunc(low, fr.types.inttype, "") } else { low = llvm.ConstNull(fr.types.inttype) } if !high.IsNil() { high = fr.createZExtOrTrunc(high, fr.types.inttype, "") } if !max.IsNil() { max = fr.createZExtOrTrunc(max, fr.types.inttype, "") } var arrayptr, arraylen, arraycap llvm.Value var elemtyp types.Type var errcode uint64 switch typ := xtyp.Underlying().(type) { case *types.Pointer: // *array errcode = gccgoRuntimeErrorARRAY_SLICE_OUT_OF_BOUNDS arraytyp := typ.Elem().Underlying().(*types.Array) elemtyp = arraytyp.Elem() arrayptr = x arrayptr = fr.builder.CreateBitCast(arrayptr, llvm.PointerType(llvm.Int8Type(), 0), "") arraylen = llvm.ConstInt(fr.llvmtypes.inttype, uint64(arraytyp.Len()), false) arraycap = arraylen case *types.Slice: errcode = gccgoRuntimeErrorSLICE_SLICE_OUT_OF_BOUNDS elemtyp = typ.Elem() arrayptr = fr.builder.CreateExtractValue(x, 0, "") arraylen = fr.builder.CreateExtractValue(x, 1, "") arraycap = fr.builder.CreateExtractValue(x, 2, "") case *types.Basic: if high.IsNil() { high = llvm.ConstAllOnes(fr.types.inttype) // -1 } result := fr.runtime.stringSlice.call(fr, x, low, high) return result[0] default: panic("unimplemented") } if high.IsNil() { high = arraylen } if max.IsNil() { max = arraycap } // Bounds checking: 0 <= low <= high <= max <= cap zero := llvm.ConstNull(fr.types.inttype) l0 := fr.builder.CreateICmp(llvm.IntSLT, low, zero, "") hl := fr.builder.CreateICmp(llvm.IntSLT, high, low, "") mh := fr.builder.CreateICmp(llvm.IntSLT, max, high, "") cm := fr.builder.CreateICmp(llvm.IntSLT, arraycap, max, "") cond := fr.builder.CreateOr(l0, hl, "") cond = fr.builder.CreateOr(cond, mh, "") cond = fr.builder.CreateOr(cond, cm, "") fr.condBrRuntimeError(cond, errcode) slicelen := fr.builder.CreateSub(high, low, "") slicecap := fr.builder.CreateSub(max, low, "") elemsize := llvm.ConstInt(fr.llvmtypes.inttype, uint64(fr.llvmtypes.Sizeof(elemtyp)), false) offset := fr.builder.CreateMul(low, elemsize, "") sliceptr := fr.builder.CreateInBoundsGEP(arrayptr, []llvm.Value{offset}, "") llslicetyp := fr.llvmtypes.sliceBackendType().ToLLVM(fr.llvmtypes.ctx) sliceValue := llvm.Undef(llslicetyp) sliceValue = fr.builder.CreateInsertValue(sliceValue, sliceptr, 0, "") sliceValue = fr.builder.CreateInsertValue(sliceValue, slicelen, 1, "") sliceValue = fr.builder.CreateInsertValue(sliceValue, slicecap, 2, "") return sliceValue }
// mapIterNext advances the iterator, and returns the tuple (ok, k, v). func (fr *frame) mapIterNext(iter []*govalue) []*govalue { maptyp := iter[0].Type().Underlying().(*types.Map) ktyp := maptyp.Key() klltyp := fr.types.ToLLVM(ktyp) vtyp := maptyp.Elem() vlltyp := fr.types.ToLLVM(vtyp) m, isinitptr := iter[0], iter[1] i8ptr := llvm.PointerType(llvm.Int8Type(), 0) mapiterbufty := llvm.ArrayType(i8ptr, 4) mapiterbuf := fr.allocaBuilder.CreateAlloca(mapiterbufty, "") mapiterbufelem0ptr := fr.builder.CreateStructGEP(mapiterbuf, 0, "") keybuf := fr.allocaBuilder.CreateAlloca(klltyp, "") keyptr := fr.builder.CreateBitCast(keybuf, i8ptr, "") valbuf := fr.allocaBuilder.CreateAlloca(vlltyp, "") valptr := fr.builder.CreateBitCast(valbuf, i8ptr, "") isinit := fr.builder.CreateLoad(isinitptr.value, "") initbb := llvm.AddBasicBlock(fr.function, "") nextbb := llvm.AddBasicBlock(fr.function, "") contbb := llvm.AddBasicBlock(fr.function, "") fr.builder.CreateCondBr(isinit, nextbb, initbb) fr.builder.SetInsertPointAtEnd(initbb) fr.builder.CreateStore(llvm.ConstAllOnes(llvm.Int1Type()), isinitptr.value) fr.runtime.mapiterinit.call(fr, m.value, mapiterbufelem0ptr) fr.builder.CreateBr(contbb) fr.builder.SetInsertPointAtEnd(nextbb) fr.runtime.mapiternext.call(fr, mapiterbufelem0ptr) fr.builder.CreateBr(contbb) fr.builder.SetInsertPointAtEnd(contbb) mapiterbufelem0 := fr.builder.CreateLoad(mapiterbufelem0ptr, "") okbit := fr.builder.CreateIsNotNull(mapiterbufelem0, "") ok := fr.builder.CreateZExt(okbit, llvm.Int8Type(), "") loadbb := llvm.AddBasicBlock(fr.function, "") cont2bb := llvm.AddBasicBlock(fr.function, "") fr.builder.CreateCondBr(okbit, loadbb, cont2bb) fr.builder.SetInsertPointAtEnd(loadbb) fr.runtime.mapiter2.call(fr, mapiterbufelem0ptr, keyptr, valptr) loadbb = fr.builder.GetInsertBlock() loadedkey := fr.builder.CreateLoad(keybuf, "") loadedval := fr.builder.CreateLoad(valbuf, "") fr.builder.CreateBr(cont2bb) fr.builder.SetInsertPointAtEnd(cont2bb) k := fr.builder.CreatePHI(klltyp, "") k.AddIncoming( []llvm.Value{llvm.ConstNull(klltyp), loadedkey}, []llvm.BasicBlock{contbb, loadbb}, ) v := fr.builder.CreatePHI(vlltyp, "") v.AddIncoming( []llvm.Value{llvm.ConstNull(vlltyp), loadedval}, []llvm.BasicBlock{contbb, loadbb}, ) return []*govalue{newValue(ok, types.Typ[types.Bool]), newValue(k, ktyp), newValue(v, vtyp)} }
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") }