Пример #1
0
Файл: stmt.go Проект: spate/llgo
func (c *compiler) VisitBlockStmt(stmt *ast.BlockStmt, createNewBlock bool) {
	// This is a little awkward, but it makes dealing with branching easier.
	// A free-standing block statement (i.e. one not attached to a control
	// statement) will splice in a new block.
	var doneBlock llvm.BasicBlock
	if createNewBlock {
		currBlock := c.builder.GetInsertBlock()
		doneBlock = llvm.InsertBasicBlock(currBlock, "")
		doneBlock.MoveAfter(currBlock)
		newBlock := llvm.InsertBasicBlock(doneBlock, "")
		c.builder.CreateBr(newBlock)
		c.builder.SetInsertPointAtEnd(newBlock)
	}

	for _, stmt := range stmt.List {
		c.VisitStmt(stmt)
		if _, ok := stmt.(*ast.BranchStmt); ok {
			// Ignore anything after a branch statement.
			break
		}
	}

	if createNewBlock {
		c.maybeImplicitBranch(doneBlock)
		c.builder.SetInsertPointAtEnd(doneBlock)
	}
}
Пример #2
0
func (c *compiler) VisitBlockStmt(stmt *ast.BlockStmt, createNewBlock bool) {
	// This is a little awkward, but it makes dealing with branching easier.
	// A free-standing block statement (i.e. one not attached to a control
	// statement) will splice in a new block.
	var doneBlock llvm.BasicBlock
	if createNewBlock {
		currBlock := c.builder.GetInsertBlock()
		doneBlock = llvm.InsertBasicBlock(currBlock, "")
		doneBlock.MoveAfter(currBlock)
		newBlock := llvm.InsertBasicBlock(doneBlock, "")
		c.builder.CreateBr(newBlock)
		c.builder.SetInsertPointAtEnd(newBlock)
	}

	// Visit each statement in the block. When we have a terminator,
	// ignore everything until we get to a labeled statement.
	for _, stmt := range stmt.List {
		currBlock := c.builder.GetInsertBlock()
		in := currBlock.LastInstruction()
		if in.IsNil() || in.IsATerminatorInst().IsNil() {
			c.VisitStmt(stmt)
		} else if _, ok := stmt.(*ast.LabeledStmt); ok {
			// FIXME we might end up with a labeled statement
			// with no predecessors, due to dead code elimination.
			c.VisitStmt(stmt)
		}
	}

	if createNewBlock {
		c.maybeImplicitBranch(doneBlock)
		c.builder.SetInsertPointAtEnd(doneBlock)
	}
}
Пример #3
0
// maybeImplicitBranch creates a branch from the current position to the
// specified basic block, if and only if the current basic block's last
// instruction is not a terminator.
//
// If dest is nil, then branch to the next basic block, if any.
func (c *compiler) maybeImplicitBranch(dest llvm.BasicBlock) {
	currBlock := c.builder.GetInsertBlock()
	if in := currBlock.LastInstruction(); in.IsNil() || in.IsATerminatorInst().IsNil() {
		if dest.IsNil() {
			dest = llvm.NextBasicBlock(currBlock)
			if !dest.IsNil() {
				c.builder.CreateBr(dest)
			}
		} else {
			c.builder.CreateBr(dest)
		}
	}
}
Пример #4
0
func (c *compiler) VisitIfStmt(stmt *ast.IfStmt) {
	curr_block := c.builder.GetInsertBlock()
	resume_block := llvm.AddBasicBlock(curr_block.Parent(), "endif")
	resume_block.MoveAfter(curr_block)

	var if_block, else_block llvm.BasicBlock
	if stmt.Else != nil {
		else_block = llvm.InsertBasicBlock(resume_block, "else")
		if_block = llvm.InsertBasicBlock(else_block, "if")
	} else {
		if_block = llvm.InsertBasicBlock(resume_block, "if")
	}
	if stmt.Else == nil {
		else_block = resume_block
	}

	if stmt.Init != nil {
		c.PushScope()
		c.VisitStmt(stmt.Init)
		defer c.PopScope()
	}

	cond_val := c.VisitExpr(stmt.Cond)
	c.builder.CreateCondBr(cond_val.LLVMValue(), if_block, else_block)
	c.builder.SetInsertPointAtEnd(if_block)
	c.VisitBlockStmt(stmt.Body)
	if in := if_block.LastInstruction(); in.IsNil() || in.IsATerminatorInst().IsNil() {
		c.builder.CreateBr(resume_block)
	}

	if stmt.Else != nil {
		c.builder.SetInsertPointAtEnd(else_block)
		c.VisitStmt(stmt.Else)
		if in := else_block.LastInstruction(); in.IsNil() || in.IsATerminatorInst().IsNil() {
			c.builder.CreateBr(resume_block)
		}
	}

	// If there's a block after the "resume" block (i.e. a nested control
	// statement), then create a branch to it as the last instruction.
	c.builder.SetInsertPointAtEnd(resume_block)
	if last := resume_block.Parent().LastBasicBlock(); last != resume_block {
		c.builder.CreateBr(last)
		c.builder.SetInsertPointBefore(resume_block.FirstInstruction())
	}
}
Пример #5
0
// createCall emits the code for a function call, taking into account
// variadic functions, receivers, and panic/defer.
//
// dotdotdot is true if the last argument is followed with "...".
func (c *compiler) createCall(fn *LLVMValue, argValues []Value, dotdotdot, invoke bool) *LLVMValue {
	fn_type := fn.Type().Underlying().(*types.Signature)
	var args []llvm.Value

	// TODO Move all of this to evalCallArgs?
	params := fn_type.Params()
	if nparams := int(params.Len()); nparams > 0 {
		if fn_type.IsVariadic() {
			nparams--
		}
		for i := 0; i < nparams; i++ {
			value := argValues[i]
			args = append(args, value.LLVMValue())
		}
		if fn_type.IsVariadic() {
			if dotdotdot {
				// Calling f(x...). Just pass the slice directly.
				slice_value := argValues[nparams].LLVMValue()
				args = append(args, slice_value)
			} else {
				varargs := make([]llvm.Value, len(argValues)-nparams)
				for i, value := range argValues[nparams:] {
					varargs[i] = value.LLVMValue()
				}
				param_type := params.At(nparams).Type().(*types.Slice).Elem()
				slice_value := c.makeLiteralSlice(varargs, param_type)
				args = append(args, slice_value)
			}
		}
	}

	var result_type types.Type
	switch results := fn_type.Results(); results.Len() {
	case 0: // no-op
	case 1:
		result_type = results.At(0).Type()
	default:
		result_type = results
	}

	// Depending on whether the function contains defer statements or not,
	// we'll generate either a "call" or an "invoke" instruction.
	var createCall = c.builder.CreateCall
	if invoke {
		f := c.functions.top()
		// TODO Create a method on compiler (avoid creating closures).
		createCall = func(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
			currblock := c.builder.GetInsertBlock()
			returnblock := llvm.AddBasicBlock(currblock.Parent(), "")
			returnblock.MoveAfter(currblock)
			value := c.builder.CreateInvoke(fn, args, returnblock, f.unwindblock, "")
			c.builder.SetInsertPointAtEnd(returnblock)
			return value
		}
	}

	var fnptr llvm.Value
	fnval := fn.LLVMValue()
	if fnval.Type().TypeKind() == llvm.PointerTypeKind {
		fnptr = fnval
	} else {
		fnptr = c.builder.CreateExtractValue(fnval, 0, "")
		context := c.builder.CreateExtractValue(fnval, 1, "")
		fntyp := fnptr.Type().ElementType()
		paramTypes := fntyp.ParamTypes()

		// If the context is not a constant null, and we're not
		// dealing with a method (where we don't care about the value
		// of the receiver), then we must conditionally call the
		// function with the additional receiver/closure.
		if !context.IsNull() || fn_type.Recv() != nil {
			// Store the blocks for referencing in the Phi below;
			// note that we update the block after each createCall,
			// since createCall may create new blocks and we want
			// the predecessors to the Phi.
			var nullctxblock llvm.BasicBlock
			var nonnullctxblock llvm.BasicBlock
			var endblock llvm.BasicBlock
			var nullctxresult llvm.Value

			// len(paramTypes) == len(args) iff function is not a method.
			if !context.IsConstant() && len(paramTypes) == len(args) {
				currblock := c.builder.GetInsertBlock()
				endblock = llvm.AddBasicBlock(currblock.Parent(), "")
				endblock.MoveAfter(currblock)
				nonnullctxblock = llvm.InsertBasicBlock(endblock, "")
				nullctxblock = llvm.InsertBasicBlock(nonnullctxblock, "")
				nullctx := c.builder.CreateIsNull(context, "")
				c.builder.CreateCondBr(nullctx, nullctxblock, nonnullctxblock)

				// null context case.
				c.builder.SetInsertPointAtEnd(nullctxblock)
				nullctxresult = createCall(fnptr, args, "")
				nullctxblock = c.builder.GetInsertBlock()
				c.builder.CreateBr(endblock)
				c.builder.SetInsertPointAtEnd(nonnullctxblock)
			}

			// non-null context case.
			var result llvm.Value
			args := append([]llvm.Value{context}, args...)
			if len(paramTypes) < len(args) {
				returnType := fntyp.ReturnType()
				ctxType := context.Type()
				paramTypes := append([]llvm.Type{ctxType}, paramTypes...)
				vararg := fntyp.IsFunctionVarArg()
				fntyp := llvm.FunctionType(returnType, paramTypes, vararg)
				fnptrtyp := llvm.PointerType(fntyp, 0)
				fnptr = c.builder.CreateBitCast(fnptr, fnptrtyp, "")
			}
			result = createCall(fnptr, args, "")

			// If the return type is not void, create a
			// PHI node to select which value to return.
			if !nullctxresult.IsNil() {
				nonnullctxblock = c.builder.GetInsertBlock()
				c.builder.CreateBr(endblock)
				c.builder.SetInsertPointAtEnd(endblock)
				if result.Type().TypeKind() != llvm.VoidTypeKind {
					phiresult := c.builder.CreatePHI(result.Type(), "")
					values := []llvm.Value{nullctxresult, result}
					blocks := []llvm.BasicBlock{nullctxblock, nonnullctxblock}
					phiresult.AddIncoming(values, blocks)
					result = phiresult
				}
			}
			return c.NewValue(result, result_type)
		}
	}
	result := createCall(fnptr, args, "")
	return c.NewValue(result, result_type)
}