Example #1
0
File: ssa.go Project: hinike/llgo
// If this value is sufficiently large, look through referrers to see if we can
// avoid a load.
func (fr *frame) canAvoidLoad(instr *ssa.UnOp, op llvm.Value) bool {
	if fr.types.Sizeof(instr.Type()) < 16 {
		// Don't bother with small values.
		return false
	}

	// Keep track of whether our pointer may escape. We conservatively assume
	// that MakeInterfaces will escape.
	esc := false

	// We only know how to avoid loads if they are used to create an interface
	// or read an element of the structure. If we see any other referrer, abort.
	for _, ref := range *instr.Referrers() {
		switch ref.(type) {
		case *ssa.MakeInterface:
			esc = true
		case *ssa.Field, *ssa.Index:
			// ok
		default:
			return false
		}
	}

	var opcopy llvm.Value
	if esc {
		opcopy = fr.createTypeMalloc(instr.Type())
	} else {
		opcopy = fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(instr.Type()), "")
	}
	fr.memcpy(opcopy, op, llvm.ConstInt(fr.types.inttype, uint64(fr.types.Sizeof(instr.Type())), false))

	fr.ptr[instr] = opcopy
	return true
}
Example #2
0
func visitDeref(inst *ssa.UnOp, fr *frame) {
	ptr := inst.X
	val := inst

	if _, ok := ptr.(*ssa.Global); ok {
		fr.locals[ptr] = fr.env.globals[ptr]
		fmt.Fprintf(os.Stderr, "   %s = *%s (global) of type %s\n", cyan(reg(val)), ptr.Name(), ptr.Type().String())
		fmt.Fprintf(os.Stderr, "    ^ i.e. %s\n", fr.locals[ptr].String())

		switch deref(fr.locals[ptr].Var.Type()).(type) {
		case *types.Array, *types.Slice:
			if _, ok := fr.env.arrays[fr.env.globals[ptr]]; !ok {
				fr.env.arrays[fr.env.globals[ptr]] = make(Elems)
			}

		case *types.Struct:
			if _, ok := fr.env.structs[fr.env.globals[ptr]]; !ok {
				fr.env.structs[fr.env.globals[ptr]] = make(Fields)
			}
		}
	}

	switch vd, kind := fr.get(ptr); kind {
	case Array, LocalArray:
		fr.locals[val] = vd
		fmt.Fprintf(os.Stderr, "   %s = *%s (array)\n", cyan(reg(val)), ptr.Name())

	case Struct, LocalStruct:
		fr.locals[val] = vd
		fmt.Fprintf(os.Stderr, "   %s = *%s (struct)\n", cyan(reg(val)), ptr.Name())

	case Chan:
		fr.locals[val] = vd
		fmt.Fprintf(os.Stderr, "   %s = *%s (previously initalised Chan)\n", cyan(reg(val)), ptr.Name())

	case Nothing:
		fmt.Fprintf(os.Stderr, "   # %s = *%s (not found)\n", red(inst.String()), red(inst.X.String()))
		if _, ok := val.Type().Underlying().(*types.Chan); ok {
			fmt.Fprintf(os.Stderr, "     ^ channel (not allocated, must be initialised by MakeChan)")
		}

	default:
		fmt.Fprintf(os.Stderr, "   # %s = *%s/%s (not found, type=%s)\n", red(inst.String()), red(inst.X.String()), reg(inst.X), inst.Type().String())
	}
}
Example #3
0
func (f *Function) UnOp(instr *ssa.UnOp) (string, *Error) {
	var err *Error
	asm := ""
	switch instr.Op {
	default:
		ice(fmt.Sprintf("unknown Op token (%v): \"%v\"", instr.Op, instr))
	case token.NOT: // logical negation
		asm, err = f.UnOpXor(instr, 1)
	case token.XOR: //bitwise negation
		asm, err = f.UnOpXor(instr, -1)
	case token.SUB: // arithmetic negation e.g. x=>-x
		asm, err = f.UnOpSub(instr)
	case token.MUL: //pointer indirection
		asm, err = f.UnOpPointer(instr)
	}
	asm = fmt.Sprintf("// BEGIN ssa.UnOp: %v = %v\n", instr.Name(), instr) + asm
	asm += fmt.Sprintf("// END ssa.UnOp: %v = %v\n", instr.Name(), instr)
	return asm, err
}
Example #4
0
// bitwise negation
func (f *Function) UnOpXor(instr *ssa.UnOp, xorVal int32) (string, *Error) {
	ctx := context{f, instr}
	if ident := f.Ident(instr); ident == nil {
		return ErrorMsg(fmt.Sprintf("Cannot alloc value: %v", instr))
	}

	addr, ok := f.identifiers[instr.Name()]
	if !ok {
		msg := fmt.Sprintf("unknown name (%v), instr (%v)\n", instr.Name(), instr)
		ice(msg)
	}

	asm, reg, err := f.LoadValueSimple(instr, instr.X)
	if err != nil {
		return asm, err
	}

	if reg.size() < 8 {
		asm += XorImm32Reg(ctx, xorVal, reg, reg.size(), true)
	} else {
		asm += XorImm64Reg(ctx, int64(xorVal), reg, reg.size(), true)
	}

	a, err := f.StoreValue(instr, addr, reg)
	f.freeReg(reg)

	if err != nil {
		return asm, err
	} else {
		asm += a
	}

	asm = fmt.Sprintf("// BEGIN ssa.UnOpNot, %v = %v\n", instr.Name(), instr) + asm
	asm += fmt.Sprintf("// END ssa.UnOpNot, %v = %v\n", instr.Name(), instr)
	return asm, nil
}
Example #5
0
//pointer indirection, in assignment such as "z = *x"
func (f *Function) UnOpPointer(instr *ssa.UnOp) (string, *Error) {
	asm := ""
	assignment := f.Ident(instr)
	if assignment == nil {
		return ErrorMsg(fmt.Sprintf("Cannot alloc value: %v", instr))
	}
	xName := instr.X.Name()
	xInfo, okX := f.identifiers[xName]
	if !xInfo.isSsaLocal() && xInfo.param == nil && xInfo.ptr == nil {
		panic("unexpected nil ptr")
	} else if !xInfo.isSsaLocal() && xInfo.param == nil {
		asm += xInfo.ptr.spillAllRegisters(instr)
	}
	// TODO add complex64/128 support
	if isComplex(instr.Type()) || isComplex(instr.X.Type()) {
		return ErrorMsg("complex64/complex128 unimplemented")
	}
	if !okX {
		msgstr := "Unknown name for UnOp X (%v), instr \"(%v)\""
		ice(fmt.Sprintf(msgstr, instr.X, instr))
	}
	if xInfo.local == nil && xInfo.param == nil && !xInfo.isPointer() {
		fmtstr := "in UnOp, X (%v) isn't a pointer, X.type (%v), instr \"(%v)\""
		msg := fmt.Sprintf(fmtstr, instr.X, instr.X.Type(), instr)
		ice(msg)
	}
	_, _, size := assignment.Addr()
	if xInfo.isSsaLocal() {
		ctx := context{f, instr}
		a, reg := xInfo.load(ctx)
		asm += a
		asm += assignment.newValue(ctx, reg, 0, xInfo.size())
		f.freeReg(reg)
	} else {
		var tmpData *register
		var a1 string
		if isXmm(instr.Type()) {
			a1, tmpData = f.allocReg(instr, XMM_REG, XmmRegSize)
		} else {
			a1, tmpData = f.allocReg(instr, regType(instr.Type()), DataRegSize)
		}
		asm += a1
		var a2 string
		var tmpAddr *register
		a2, tmpAddr = f.allocReg(instr, DATA_REG, DataRegSize)
		asm += a2

		src, ok := xInfo.storage.(*memory)
		if !ok {
			ice("cannot dereference pointer to constant")
		}
		dst, ok := assignment.storage.(*memory)
		if !ok {
			ice("cannot modify constant")
		}
		dst.removeAliases()
		ctx := context{f, instr}
		a, srcReg := src.load(ctx, src.ownerRegion())
		asm += a
		aReg, aOffset, _ := assignment.Addr()
		asm += MovRegIndirectMem(ctx, dst.optype(), srcReg, assignment.name, aOffset, &aReg, size, tmpAddr, tmpData)
		dst.setInitialized(region{0, size})
		f.freeReg(srcReg)
		f.freeReg(tmpAddr)
		f.freeReg(tmpData)
	}

	asm = fmt.Sprintf("// BEGIN ssa.UnOpPointer, %v = %v\n", instr.Name(), instr) + asm
	asm += fmt.Sprintf("// END ssa.UnOpPointer, %v = %v\n", instr.Name(), instr)
	return asm, nil
}
Example #6
0
// arithmetic negation
func (f *Function) UnOpSub(instr *ssa.UnOp) (string, *Error) {
	ctx := context{f, instr}
	if ident := f.Ident(instr); ident == nil {
		return ErrorMsg(fmt.Sprintf("Cannot alloc value: %v", instr))
	}
	var regSubX, regX, regVal *register
	var a1, a2 string
	a1, regVal = f.allocReg(instr, regType(instr.Type()), f.sizeof(instr))
	a2, regSubX = f.allocReg(instr, regType(instr.X.Type()), f.sizeof(instr.X))

	addr, ok := f.identifiers[instr.Name()]
	if !ok {
		msg := fmt.Sprintf("unknown name (%v), instr (%v)\n", instr.Name(), instr)
		ice(msg)
	}

	a3, regX, err := f.LoadValueSimple(instr, instr.X)
	asm := a1 + a2 + a3
	if err != nil {
		return asm, err
	}

	asm += ZeroReg(ctx, regSubX)
	optypes := GetOpDataType(instr.Type())
	asm += ArithOp(ctx, optypes, token.SUB, regSubX, regX, regVal)
	f.freeReg(regX)
	f.freeReg(regSubX)

	a, err := f.StoreValue(instr, addr, regVal)
	if err != nil {
		return asm, err
	} else {
		asm += a
	}

	f.freeReg(regVal)
	asm = fmt.Sprintf("// BEGIN ssa.UnOpSub, %v = %v\n", instr.Name(), instr) + asm
	asm += fmt.Sprintf("// END ssa.UnOpSub, %v = %v\n", instr.Name(), instr)
	return asm, nil

}