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