compiler: refactor parseTypeAssert
Move to the builder object, and rename to createTypeAssert.
Этот коммит содержится в:
родитель
349ecf1736
коммит
7733666fa8
3 изменённых файлов: 32 добавлений и 27 удалений
|
@ -1897,7 +1897,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
return llvm.Value{}, c.makeError(expr.Pos(), "unknown slice type: "+typ.String())
|
||||
}
|
||||
case *ssa.TypeAssert:
|
||||
return c.parseTypeAssert(frame, expr), nil
|
||||
return frame.createTypeAssert(expr), nil
|
||||
case *ssa.UnOp:
|
||||
return c.parseUnOp(frame, expr)
|
||||
default:
|
||||
|
|
|
@ -271,7 +271,7 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value {
|
|||
// getInterfaceMethodSet returns a global variable with the method set of the
|
||||
// given named interface type. This method set is used by the interface lowering
|
||||
// pass.
|
||||
func (c *Compiler) getInterfaceMethodSet(typ *types.Named) llvm.Value {
|
||||
func (c *compilerContext) getInterfaceMethodSet(typ *types.Named) llvm.Value {
|
||||
global := c.mod.NamedGlobal(typ.String() + "$interface")
|
||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||
if !global.IsNil() {
|
||||
|
@ -297,7 +297,7 @@ func (c *Compiler) getInterfaceMethodSet(typ *types.Named) llvm.Value {
|
|||
// getMethodSignature returns a global variable which is a reference to an
|
||||
// external *i8 indicating the indicating the signature of this method. It is
|
||||
// used during the interface lowering pass.
|
||||
func (c *Compiler) getMethodSignature(method *types.Func) llvm.Value {
|
||||
func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
|
||||
signature := ir.MethodSignature(method)
|
||||
signatureGlobal := c.mod.NamedGlobal("func " + signature)
|
||||
if signatureGlobal.IsNil() {
|
||||
|
@ -307,18 +307,18 @@ func (c *Compiler) getMethodSignature(method *types.Func) llvm.Value {
|
|||
return signatureGlobal
|
||||
}
|
||||
|
||||
// parseTypeAssert will emit the code for a typeassert, used in if statements
|
||||
// createTypeAssert will emit the code for a typeassert, used in if statements
|
||||
// and in type switches (Go SSA does not have type switches, only if/else
|
||||
// chains). Note that even though the Go SSA does not contain type switches,
|
||||
// LLVM will recognize the pattern and make it a real switch in many cases.
|
||||
//
|
||||
// Type asserts on concrete types are trivial: just compare type numbers. Type
|
||||
// asserts on interfaces are more difficult, see the comments in the function.
|
||||
func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) llvm.Value {
|
||||
itf := frame.getValue(expr.X)
|
||||
assertedType := c.getLLVMType(expr.AssertedType)
|
||||
func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
itf := b.getValue(expr.X)
|
||||
assertedType := b.getLLVMType(expr.AssertedType)
|
||||
|
||||
actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type")
|
||||
actualTypeNum := b.CreateExtractValue(itf, 0, "interface.type")
|
||||
commaOk := llvm.Value{}
|
||||
if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
||||
// Type assert on interface type.
|
||||
|
@ -329,15 +329,15 @@ func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) llvm.Valu
|
|||
// the main Go compiler, where the runtime checks whether the type
|
||||
// implements each method of the interface. See:
|
||||
// https://research.swtch.com/interfaces
|
||||
methodSet := c.getInterfaceMethodSet(expr.AssertedType.(*types.Named))
|
||||
commaOk = c.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "")
|
||||
methodSet := b.getInterfaceMethodSet(expr.AssertedType.(*types.Named))
|
||||
commaOk = b.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "")
|
||||
|
||||
} else {
|
||||
// Type assert on concrete type.
|
||||
// Call runtime.typeAssert, which will be lowered to a simple icmp or
|
||||
// const false in the interface lowering pass.
|
||||
assertedTypeCodeGlobal := c.getTypeCode(expr.AssertedType)
|
||||
commaOk = c.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode")
|
||||
assertedTypeCodeGlobal := b.getTypeCode(expr.AssertedType)
|
||||
commaOk = b.createRuntimeCall("typeAssert", []llvm.Value{actualTypeNum, assertedTypeCodeGlobal}, "typecode")
|
||||
}
|
||||
|
||||
// Add 2 new basic blocks (that should get optimized away): one for the
|
||||
|
@ -351,15 +351,15 @@ func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) llvm.Valu
|
|||
// typeassert should return a zero value, not an incorrectly casted
|
||||
// value.
|
||||
|
||||
prevBlock := c.builder.GetInsertBlock()
|
||||
okBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.ok")
|
||||
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.next")
|
||||
frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||
c.builder.CreateCondBr(commaOk, okBlock, nextBlock)
|
||||
prevBlock := b.GetInsertBlock()
|
||||
okBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, "typeassert.ok")
|
||||
nextBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, "typeassert.next")
|
||||
b.blockExits[b.currentBlock] = nextBlock // adjust outgoing block for phi nodes
|
||||
b.CreateCondBr(commaOk, okBlock, nextBlock)
|
||||
|
||||
// Retrieve the value from the interface if the type assert was
|
||||
// successful.
|
||||
c.builder.SetInsertPointAtEnd(okBlock)
|
||||
b.SetInsertPointAtEnd(okBlock)
|
||||
var valueOk llvm.Value
|
||||
if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
||||
// Type assert on interface type. Easy: just return the same
|
||||
|
@ -368,25 +368,25 @@ func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) llvm.Valu
|
|||
} else {
|
||||
// Type assert on concrete type. Extract the underlying type from
|
||||
// the interface (but only after checking it matches).
|
||||
valuePtr := c.builder.CreateExtractValue(itf, 1, "typeassert.value.ptr")
|
||||
valueOk = c.emitPointerUnpack(valuePtr, []llvm.Type{assertedType})[0]
|
||||
valuePtr := b.CreateExtractValue(itf, 1, "typeassert.value.ptr")
|
||||
valueOk = b.emitPointerUnpack(valuePtr, []llvm.Type{assertedType})[0]
|
||||
}
|
||||
c.builder.CreateBr(nextBlock)
|
||||
b.CreateBr(nextBlock)
|
||||
|
||||
// Continue after the if statement.
|
||||
c.builder.SetInsertPointAtEnd(nextBlock)
|
||||
phi := c.builder.CreatePHI(assertedType, "typeassert.value")
|
||||
b.SetInsertPointAtEnd(nextBlock)
|
||||
phi := b.CreatePHI(assertedType, "typeassert.value")
|
||||
phi.AddIncoming([]llvm.Value{llvm.ConstNull(assertedType), valueOk}, []llvm.BasicBlock{prevBlock, okBlock})
|
||||
|
||||
if expr.CommaOk {
|
||||
tuple := c.ctx.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(c.ctx.Int1Type())}, false) // create empty tuple
|
||||
tuple = c.builder.CreateInsertValue(tuple, phi, 0, "") // insert value
|
||||
tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") // insert 'comma ok' boolean
|
||||
tuple := b.ctx.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(b.ctx.Int1Type())}, false) // create empty tuple
|
||||
tuple = b.CreateInsertValue(tuple, phi, 0, "") // insert value
|
||||
tuple = b.CreateInsertValue(tuple, commaOk, 1, "") // insert 'comma ok' boolean
|
||||
return tuple
|
||||
} else {
|
||||
// This is kind of dirty as the branch above becomes mostly useless,
|
||||
// but hopefully this gets optimized away.
|
||||
c.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "")
|
||||
b.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "")
|
||||
return phi
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,11 @@ func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []l
|
|||
return llvmutil.EmitPointerUnpack(c.builder, c.mod, ptr, valueTypes)
|
||||
}
|
||||
|
||||
// emitPointerUnpack extracts a list of values packed using emitPointerPack.
|
||||
func (b *builder) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
|
||||
return llvmutil.EmitPointerUnpack(b.Builder, b.mod, ptr, valueTypes)
|
||||
}
|
||||
|
||||
// makeGlobalArray creates a new LLVM global with the given name and integers as
|
||||
// contents, and returns the global.
|
||||
// Note that it is left with the default linkage etc., you should set
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче