transform: do not special-case zero or one implementations of a method call
This is a common case, but it also complicates the code. Removing this special case does have a negative effect on code size in rare cases, but I don't think it's worth keeping around (and possibly causing bugs) for such uncommon cases. This should not result in functional changes, although the output (as stated above) sometimes changes a little bit.
Этот коммит содержится в:
		
							родитель
							
								
									c72f9eb08c
								
							
						
					
					
						коммит
						1570adac1c
					
				
					 2 изменённых файлов: 48 добавлений и 52 удалений
				
			
		|  | @ -292,58 +292,40 @@ func (p *lowerInterfacesPass) run() error { | |||
| 
 | ||||
| 		methodSet := use.Operand(1).Operand(0) // global variable | ||||
| 		itf := p.interfaces[methodSet.Name()] | ||||
| 		if len(itf.types) == 0 { | ||||
| 			// This method call is impossible: no type implements this | ||||
| 			// interface. In fact, the previous type assert that got this | ||||
| 			// interface value should already have returned false. | ||||
| 			// Replace the function pointer with undef (which will then be | ||||
| 			// called), indicating to the optimizer this code is unreachable. | ||||
| 			use.ReplaceAllUsesWith(llvm.Undef(p.uintptrType)) | ||||
| 			use.EraseFromParentAsInstruction() | ||||
| 		} else if len(itf.types) == 1 { | ||||
| 			// There is only one implementation of the given type. | ||||
| 			// Call that function directly. | ||||
| 			err := p.replaceInvokeWithCall(use, itf.types[0], signature) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} else { | ||||
| 			// There are multiple types implementing this interface, thus there | ||||
| 			// are multiple possible functions to call. Delegate calling the | ||||
| 			// right function to a special wrapper function. | ||||
| 			inttoptrs := getUses(use) | ||||
| 			if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() { | ||||
| 				return errorAt(use, "internal error: expected exactly one inttoptr use of runtime.interfaceMethod") | ||||
| 			} | ||||
| 			inttoptr := inttoptrs[0] | ||||
| 			calls := getUses(inttoptr) | ||||
| 			for _, call := range calls { | ||||
| 				// Set up parameters for the call. First copy the regular params... | ||||
| 				params := make([]llvm.Value, call.OperandsCount()) | ||||
| 				paramTypes := make([]llvm.Type, len(params)) | ||||
| 				for i := 0; i < len(params)-1; i++ { | ||||
| 					params[i] = call.Operand(i) | ||||
| 					paramTypes[i] = params[i].Type() | ||||
| 				} | ||||
| 				// then add the typecode to the end of the list. | ||||
| 				params[len(params)-1] = typecode | ||||
| 				paramTypes[len(params)-1] = p.uintptrType | ||||
| 
 | ||||
| 				// Create a function that redirects the call to the destination | ||||
| 				// call, after selecting the right concrete type. | ||||
| 				redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes) | ||||
| 
 | ||||
| 				// Replace the old lookup/inttoptr/call with the new call. | ||||
| 				p.builder.SetInsertPointBefore(call) | ||||
| 				retval := p.builder.CreateCall(redirector, append(params, llvm.ConstNull(llvm.PointerType(p.ctx.Int8Type(), 0))), "") | ||||
| 				if retval.Type().TypeKind() != llvm.VoidTypeKind { | ||||
| 					call.ReplaceAllUsesWith(retval) | ||||
| 				} | ||||
| 				call.EraseFromParentAsInstruction() | ||||
| 			} | ||||
| 			inttoptr.EraseFromParentAsInstruction() | ||||
| 			use.EraseFromParentAsInstruction() | ||||
| 		// Delegate calling the right function to a special wrapper function. | ||||
| 		inttoptrs := getUses(use) | ||||
| 		if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() { | ||||
| 			return errorAt(use, "internal error: expected exactly one inttoptr use of runtime.interfaceMethod") | ||||
| 		} | ||||
| 		inttoptr := inttoptrs[0] | ||||
| 		calls := getUses(inttoptr) | ||||
| 		for _, call := range calls { | ||||
| 			// Set up parameters for the call. First copy the regular params... | ||||
| 			params := make([]llvm.Value, call.OperandsCount()) | ||||
| 			paramTypes := make([]llvm.Type, len(params)) | ||||
| 			for i := 0; i < len(params)-1; i++ { | ||||
| 				params[i] = call.Operand(i) | ||||
| 				paramTypes[i] = params[i].Type() | ||||
| 			} | ||||
| 			// then add the typecode to the end of the list. | ||||
| 			params[len(params)-1] = typecode | ||||
| 			paramTypes[len(params)-1] = p.uintptrType | ||||
| 
 | ||||
| 			// Create a function that redirects the call to the destination | ||||
| 			// call, after selecting the right concrete type. | ||||
| 			redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes) | ||||
| 
 | ||||
| 			// Replace the old lookup/inttoptr/call with the new call. | ||||
| 			p.builder.SetInsertPointBefore(call) | ||||
| 			retval := p.builder.CreateCall(redirector, append(params, llvm.ConstNull(llvm.PointerType(p.ctx.Int8Type(), 0))), "") | ||||
| 			if retval.Type().TypeKind() != llvm.VoidTypeKind { | ||||
| 				call.ReplaceAllUsesWith(retval) | ||||
| 			} | ||||
| 			call.EraseFromParentAsInstruction() | ||||
| 		} | ||||
| 		inttoptr.EraseFromParentAsInstruction() | ||||
| 		use.EraseFromParentAsInstruction() | ||||
| 	} | ||||
| 
 | ||||
| 	// Replace all typeasserts on interface types with matches on their concrete | ||||
|  |  | |||
							
								
								
									
										18
									
								
								transform/testdata/interface.out.ll
									
										
									
									
										предоставленный
									
									
								
							
							
						
						
									
										18
									
								
								transform/testdata/interface.out.ll
									
										
									
									
										предоставленный
									
									
								
							|  | @ -47,8 +47,8 @@ typeswitch.notUnmatched:                          ; preds = %0 | |||
|   br i1 %typeassert.ok, label %typeswitch.Doubler, label %typeswitch.notDoubler | ||||
| 
 | ||||
| typeswitch.Doubler:                               ; preds = %typeswitch.notUnmatched | ||||
|   %doubler.result = call i32 @"(Number).Double$invoke"(i8* %value, i8* null) | ||||
|   call void @runtime.printint32(i32 %doubler.result) | ||||
|   %1 = call i32 @"(Doubler).Double"(i8* %value, i8* null, i32 %typecode, i8* null) | ||||
|   call void @runtime.printint32(i32 %1) | ||||
|   ret void | ||||
| 
 | ||||
| typeswitch.notDoubler:                            ; preds = %typeswitch.notUnmatched | ||||
|  | @ -76,6 +76,20 @@ define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %parentHandle) { | |||
|   ret i32 %ret | ||||
| } | ||||
| 
 | ||||
| define internal i32 @"(Doubler).Double"(i8* %0, i8* %1, i32 %actualType, i8* %parentHandle) unnamed_addr { | ||||
| entry: | ||||
|   switch i32 %actualType, label %default [ | ||||
|     i32 68, label %"reflect/types.type:named:Number" | ||||
|   ] | ||||
| 
 | ||||
| default:                                          ; preds = %entry | ||||
|   unreachable | ||||
| 
 | ||||
| "reflect/types.type:named:Number":                ; preds = %entry | ||||
|   %2 = call i32 @"(Number).Double$invoke"(i8* %0, i8* %1) | ||||
|   ret i32 %2 | ||||
| } | ||||
| 
 | ||||
| define internal i1 @"Doubler$typeassert"(i32 %actualType) unnamed_addr { | ||||
| entry: | ||||
|   switch i32 %actualType, label %else [ | ||||
|  |  | |||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem