Example #1
0
File: value.go Project: hinike/llgo
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))
	}
}
Example #2
0
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
}
Example #3
0
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
}
Example #4
0
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
}
Example #5
0
// 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)}
}
Example #6
0
File: value.go Project: hinike/llgo
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")
}