compiler: move LLVM math builtin support into the compiler
This simplifies src/runtime/math.go, which I eventually want to remove entirely by moving the given functionality into the compiler.
Этот коммит содержится в:
		
							родитель
							
								
									ca7c849da3
								
							
						
					
					
						коммит
						58565b42cc
					
				
					 7 изменённых файлов: 138 добавлений и 37 удалений
				
			
		|  | @ -23,7 +23,7 @@ import ( | |||
| // Version of the compiler pacakge. Must be incremented each time the compiler | ||||
| // package changes in a way that affects the generated LLVM module. | ||||
| // This version is independent of the TinyGo version number. | ||||
| const Version = 12 // last change: implement syscall.rawSyscallNoError | ||||
| const Version = 13 // last change: implement LLVM math builtins in the compiler | ||||
| 
 | ||||
| func init() { | ||||
| 	llvm.InitializeAllTargets() | ||||
|  | @ -1298,6 +1298,11 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) | |||
| 			return b.createMemoryCopyCall(fn, instr.Args) | ||||
| 		case name == "runtime.memzero": | ||||
| 			return b.createMemoryZeroCall(instr.Args) | ||||
| 		case name == "math.Ceil" || name == "math.Floor" || name == "math.Sqrt" || name == "math.Trunc": | ||||
| 			result, ok := b.createMathOp(instr) | ||||
| 			if ok { | ||||
| 				return result, nil | ||||
| 			} | ||||
| 		case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm": | ||||
| 			return b.createInlineAsm(instr.Args) | ||||
| 		case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull": | ||||
|  |  | |||
|  | @ -48,6 +48,8 @@ func TestCompiler(t *testing.T) { | |||
| 		{"pragma.go", ""}, | ||||
| 		{"goroutine.go", "wasm"}, | ||||
| 		{"goroutine.go", "cortex-m-qemu"}, | ||||
| 		{"intrinsics.go", "cortex-m-qemu"}, | ||||
| 		{"intrinsics.go", "wasm"}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tc := range tests { | ||||
|  |  | |||
|  | @ -48,3 +48,57 @@ func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) { | |||
| 	b.CreateCall(llvmFn, params, "") | ||||
| 	return llvm.Value{}, nil | ||||
| } | ||||
| 
 | ||||
| var mathToLLVMMapping = map[string]string{ | ||||
| 	"math.Sqrt":  "llvm.sqrt.f64", | ||||
| 	"math.Floor": "llvm.floor.f64", | ||||
| 	"math.Ceil":  "llvm.ceil.f64", | ||||
| 	"math.Trunc": "llvm.trunc.f64", | ||||
| } | ||||
| 
 | ||||
| // createMathOp tries to lower the given call as a LLVM math intrinsic, if | ||||
| // possible. It returns the call result if possible, and a boolean whether it | ||||
| // succeeded. If it doesn't succeed, the architecture doesn't support the given | ||||
| // intrinsic. | ||||
| func (b *builder) createMathOp(call *ssa.CallCommon) (llvm.Value, bool) { | ||||
| 	// Check whether this intrinsic is supported on the given GOARCH. | ||||
| 	// If it is unsupported, this can have two reasons: | ||||
| 	// | ||||
| 	//  1. LLVM can expand the intrinsic inline (using float instructions), but | ||||
| 	//     the result doesn't pass the tests of the math package. | ||||
| 	//  2. LLVM cannot expand the intrinsic inline, will therefore lower it as a | ||||
| 	//     libm function call, but the libm function call also fails the math | ||||
| 	//     package tests. | ||||
| 	// | ||||
| 	// Whatever the implementation, it must pass the tests in the math package | ||||
| 	// so unfortunately only the below intrinsic+architecture combinations are | ||||
| 	// supported. | ||||
| 	name := call.StaticCallee().RelString(nil) | ||||
| 	switch name { | ||||
| 	case "math.Ceil", "math.Floor", "math.Trunc": | ||||
| 		if b.GOARCH != "wasm" && b.GOARCH != "arm64" { | ||||
| 			return llvm.Value{}, false | ||||
| 		} | ||||
| 	case "math.Sqrt": | ||||
| 		if b.GOARCH != "wasm" && b.GOARCH != "amd64" && b.GOARCH != "386" { | ||||
| 			return llvm.Value{}, false | ||||
| 		} | ||||
| 	default: | ||||
| 		return llvm.Value{}, false // only the above functions are supported. | ||||
| 	} | ||||
| 
 | ||||
| 	llvmFn := b.mod.NamedFunction(mathToLLVMMapping[name]) | ||||
| 	if llvmFn.IsNil() { | ||||
| 		// The intrinsic doesn't exist yet, so declare it. | ||||
| 		// At the moment, all supported intrinsics have the form "double | ||||
| 		// foo(double %x)" so we can hardcode the signature here. | ||||
| 		llvmType := llvm.FunctionType(b.ctx.DoubleType(), []llvm.Type{b.ctx.DoubleType()}, false) | ||||
| 		llvmFn = llvm.AddFunction(b.mod, mathToLLVMMapping[name], llvmType) | ||||
| 	} | ||||
| 	// Create a call to the intrinsic. | ||||
| 	args := make([]llvm.Value, len(call.Args)) | ||||
| 	for i, arg := range call.Args { | ||||
| 		args[i] = b.getValue(arg) | ||||
| 	} | ||||
| 	return b.CreateCall(llvmFn, args, ""), true | ||||
| } | ||||
|  |  | |||
							
								
								
									
										27
									
								
								compiler/testdata/intrinsics-cortex-m-qemu.ll
									
										
									
									
										предоставленный
									
									
										Обычный файл
									
								
							
							
						
						
									
										27
									
								
								compiler/testdata/intrinsics-cortex-m-qemu.ll
									
										
									
									
										предоставленный
									
									
										Обычный файл
									
								
							|  | @ -0,0 +1,27 @@ | |||
| ; ModuleID = 'intrinsics.go' | ||||
| source_filename = "intrinsics.go" | ||||
| target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" | ||||
| target triple = "armv7m-none-eabi" | ||||
| 
 | ||||
| declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) | ||||
| 
 | ||||
| define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr { | ||||
| entry: | ||||
|   ret void | ||||
| } | ||||
| 
 | ||||
| define hidden double @main.mySqrt(double %x, i8* %context, i8* %parentHandle) unnamed_addr { | ||||
| entry: | ||||
|   %0 = call double @math.Sqrt(double %x, i8* undef, i8* undef) | ||||
|   ret double %0 | ||||
| } | ||||
| 
 | ||||
| declare double @math.Sqrt(double, i8*, i8*) | ||||
| 
 | ||||
| define hidden double @main.myTrunc(double %x, i8* %context, i8* %parentHandle) unnamed_addr { | ||||
| entry: | ||||
|   %0 = call double @math.Trunc(double %x, i8* undef, i8* undef) | ||||
|   ret double %0 | ||||
| } | ||||
| 
 | ||||
| declare double @math.Trunc(double, i8*, i8*) | ||||
							
								
								
									
										31
									
								
								compiler/testdata/intrinsics-wasm.ll
									
										
									
									
										предоставленный
									
									
										Обычный файл
									
								
							
							
						
						
									
										31
									
								
								compiler/testdata/intrinsics-wasm.ll
									
										
									
									
										предоставленный
									
									
										Обычный файл
									
								
							|  | @ -0,0 +1,31 @@ | |||
| ; ModuleID = 'intrinsics.go' | ||||
| source_filename = "intrinsics.go" | ||||
| target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" | ||||
| target triple = "wasm32--wasi" | ||||
| 
 | ||||
| declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) | ||||
| 
 | ||||
| define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr { | ||||
| entry: | ||||
|   ret void | ||||
| } | ||||
| 
 | ||||
| define hidden double @main.mySqrt(double %x, i8* %context, i8* %parentHandle) unnamed_addr { | ||||
| entry: | ||||
|   %0 = call double @llvm.sqrt.f64(double %x) | ||||
|   ret double %0 | ||||
| } | ||||
| 
 | ||||
| ; Function Attrs: nounwind readnone speculatable willreturn | ||||
| declare double @llvm.sqrt.f64(double) #0 | ||||
| 
 | ||||
| define hidden double @main.myTrunc(double %x, i8* %context, i8* %parentHandle) unnamed_addr { | ||||
| entry: | ||||
|   %0 = call double @llvm.trunc.f64(double %x) | ||||
|   ret double %0 | ||||
| } | ||||
| 
 | ||||
| ; Function Attrs: nounwind readnone speculatable willreturn | ||||
| declare double @llvm.trunc.f64(double) #0 | ||||
| 
 | ||||
| attributes #0 = { nounwind readnone speculatable willreturn } | ||||
							
								
								
									
										14
									
								
								compiler/testdata/intrinsics.go
									
										
									
									
										предоставленный
									
									
										Обычный файл
									
								
							
							
						
						
									
										14
									
								
								compiler/testdata/intrinsics.go
									
										
									
									
										предоставленный
									
									
										Обычный файл
									
								
							|  | @ -0,0 +1,14 @@ | |||
| package main | ||||
| 
 | ||||
| // Test how intrinsics are lowered: either as regular calls to the math | ||||
| // functions or as LLVM builtins (such as llvm.sqrt.f64). | ||||
| 
 | ||||
| import "math" | ||||
| 
 | ||||
| func mySqrt(x float64) float64 { | ||||
| 	return math.Sqrt(x) | ||||
| } | ||||
| 
 | ||||
| func myTrunc(x float64) float64 { | ||||
| 	return math.Trunc(x) | ||||
| } | ||||
|  | @ -56,15 +56,7 @@ func math_Cbrt(x float64) float64 { return math_cbrt(x) } | |||
| func math_cbrt(x float64) float64 | ||||
| 
 | ||||
| //go:linkname math_Ceil math.Ceil | ||||
| func math_Ceil(x float64) float64 { | ||||
| 	if GOARCH == "arm64" || GOARCH == "wasm" { | ||||
| 		return llvm_ceil(x) | ||||
| 	} | ||||
| 	return math_ceil(x) | ||||
| } | ||||
| 
 | ||||
| //export llvm.ceil.f64 | ||||
| func llvm_ceil(x float64) float64 | ||||
| func math_Ceil(x float64) float64 { return math_ceil(x) } | ||||
| 
 | ||||
| //go:linkname math_ceil math.ceil | ||||
| func math_ceil(x float64) float64 | ||||
|  | @ -112,15 +104,7 @@ func math_Exp2(x float64) float64 { return math_exp2(x) } | |||
| func math_exp2(x float64) float64 | ||||
| 
 | ||||
| //go:linkname math_Floor math.Floor | ||||
| func math_Floor(x float64) float64 { | ||||
| 	if GOARCH == "arm64" || GOARCH == "wasm" { | ||||
| 		return llvm_floor(x) | ||||
| 	} | ||||
| 	return math_floor(x) | ||||
| } | ||||
| 
 | ||||
| //export llvm.floor.f64 | ||||
| func llvm_floor(x float64) float64 | ||||
| func math_Floor(x float64) float64 { return math_floor(x) } | ||||
| 
 | ||||
| //go:linkname math_floor math.floor | ||||
| func math_floor(x float64) float64 | ||||
|  | @ -216,15 +200,7 @@ func math_Sinh(x float64) float64 { return math_sinh(x) } | |||
| func math_sinh(x float64) float64 | ||||
| 
 | ||||
| //go:linkname math_Sqrt math.Sqrt | ||||
| func math_Sqrt(x float64) float64 { | ||||
| 	if GOARCH == "386" || GOARCH == "amd64" || GOARCH == "wasm" { | ||||
| 		return llvm_sqrt(x) | ||||
| 	} | ||||
| 	return math_sqrt(x) | ||||
| } | ||||
| 
 | ||||
| //export llvm.sqrt.f64 | ||||
| func llvm_sqrt(x float64) float64 | ||||
| func math_Sqrt(x float64) float64 { return math_sqrt(x) } | ||||
| 
 | ||||
| //go:linkname math_sqrt math.sqrt | ||||
| func math_sqrt(x float64) float64 | ||||
|  | @ -242,15 +218,7 @@ func math_Tanh(x float64) float64 { return math_tanh(x) } | |||
| func math_tanh(x float64) float64 | ||||
| 
 | ||||
| //go:linkname math_Trunc math.Trunc | ||||
| func math_Trunc(x float64) float64 { | ||||
| 	if GOARCH == "arm64" || GOARCH == "wasm" { | ||||
| 		return llvm_trunc(x) | ||||
| 	} | ||||
| 	return math_trunc(x) | ||||
| } | ||||
| 
 | ||||
| //export llvm.trunc.f64 | ||||
| func llvm_trunc(x float64) float64 | ||||
| func math_Trunc(x float64) float64 { return math_trunc(x) } | ||||
| 
 | ||||
| //go:linkname math_trunc math.trunc | ||||
| func math_trunc(x float64) float64 | ||||
|  |  | |||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem