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 | ||||||
|  |  | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem