compiler: really define runtime.mem* as LLVM intrinsic wrappers

This makes it possible to //go:linkname them from other places, like in
the reflect package. And is in my opinion a much cleaner solution.
Этот коммит содержится в:
Ayke van Laethem 2022-06-20 15:19:57 +02:00 коммит произвёл Ron Evans
родитель 1ceb63d14c
коммит 4262f0ff1f
4 изменённых файлов: 23 добавлений и 22 удалений

Просмотреть файл

@ -1619,10 +1619,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
// applied) function call. If it is anonymous, it may be a closure. // applied) function call. If it is anonymous, it may be a closure.
name := fn.RelString(nil) name := fn.RelString(nil)
switch { switch {
case name == "runtime.memcpy" || name == "runtime.memmove" || name == "reflect.memcpy":
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": case name == "math.Ceil" || name == "math.Floor" || name == "math.Sqrt" || name == "math.Trunc":
result, ok := b.createMathOp(instr) result, ok := b.createMathOp(instr)
if ok { if ok {

Просмотреть файл

@ -20,6 +20,10 @@ import (
func (b *builder) defineIntrinsicFunction() { func (b *builder) defineIntrinsicFunction() {
name := b.fn.RelString(nil) name := b.fn.RelString(nil)
switch { switch {
case name == "runtime.memcpy" || name == "runtime.memmove":
b.createMemoryCopyImpl()
case name == "runtime.memzero":
b.createMemoryZeroImpl()
case strings.HasPrefix(name, "runtime/volatile.Load"): case strings.HasPrefix(name, "runtime/volatile.Load"):
b.createVolatileLoad() b.createVolatileLoad()
case strings.HasPrefix(name, "runtime/volatile.Store"): case strings.HasPrefix(name, "runtime/volatile.Store"):
@ -35,30 +39,32 @@ func (b *builder) defineIntrinsicFunction() {
} }
} }
// createMemoryCopyCall creates a call to a builtin LLVM memcpy or memmove // createMemoryCopyImpl creates a call to a builtin LLVM memcpy or memmove
// function, declaring this function if needed. These calls are treated // function, declaring this function if needed. These calls are treated
// specially by optimization passes possibly resulting in better generated code, // specially by optimization passes possibly resulting in better generated code,
// and will otherwise be lowered to regular libc memcpy/memmove calls. // and will otherwise be lowered to regular libc memcpy/memmove calls.
func (b *builder) createMemoryCopyCall(fn *ssa.Function, args []ssa.Value) (llvm.Value, error) { func (b *builder) createMemoryCopyImpl() {
fnName := "llvm." + fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth()) b.createFunctionStart()
fnName := "llvm." + b.fn.Name() + ".p0i8.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
llvmFn := b.mod.NamedFunction(fnName) llvmFn := b.mod.NamedFunction(fnName)
if llvmFn.IsNil() { if llvmFn.IsNil() {
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false) fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.i8ptrType, b.uintptrType, b.ctx.Int1Type()}, false)
llvmFn = llvm.AddFunction(b.mod, fnName, fnType) llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
} }
var params []llvm.Value var params []llvm.Value
for _, param := range args { for _, param := range b.fn.Params {
params = append(params, b.getValue(param)) params = append(params, b.getValue(param))
} }
params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false)) params = append(params, llvm.ConstInt(b.ctx.Int1Type(), 0, false))
b.CreateCall(llvmFn, params, "") b.CreateCall(llvmFn, params, "")
return llvm.Value{}, nil b.CreateRetVoid()
} }
// createMemoryZeroCall creates calls to llvm.memset.* to zero a block of // createMemoryZeroImpl creates calls to llvm.memset.* to zero a block of
// memory, declaring the function if needed. These calls will be lowered to // memory, declaring the function if needed. These calls will be lowered to
// regular libc memset calls if they aren't optimized out in a different way. // regular libc memset calls if they aren't optimized out in a different way.
func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) { func (b *builder) createMemoryZeroImpl() {
b.createFunctionStart()
fnName := "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth()) fnName := "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
llvmFn := b.mod.NamedFunction(fnName) llvmFn := b.mod.NamedFunction(fnName)
if llvmFn.IsNil() { if llvmFn.IsNil() {
@ -66,13 +72,13 @@ func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
llvmFn = llvm.AddFunction(b.mod, fnName, fnType) llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
} }
params := []llvm.Value{ params := []llvm.Value{
b.getValue(args[0]), b.getValue(b.fn.Params[0]),
llvm.ConstInt(b.ctx.Int8Type(), 0, false), llvm.ConstInt(b.ctx.Int8Type(), 0, false),
b.getValue(args[1]), b.getValue(b.fn.Params[1]),
llvm.ConstInt(b.ctx.Int1Type(), 0, false), llvm.ConstInt(b.ctx.Int1Type(), 0, false),
} }
b.CreateCall(llvmFn, params, "") b.CreateCall(llvmFn, params, "")
return llvm.Value{}, nil b.CreateRetVoid()
} }
var mathToLLVMMapping = map[string]string{ var mathToLLVMMapping = map[string]string{

Просмотреть файл

@ -808,8 +808,7 @@ func (e *ValueError) Error() string {
return "reflect: call of " + e.Method + " on " + e.Kind.String() + " Value" return "reflect: call of " + e.Method + " on " + e.Kind.String() + " Value"
} }
// Calls to this function are converted to LLVM intrinsic calls such as //go:linkname memcpy runtime.memcpy
// llvm.memcpy.p0i8.p0i8.i32().
func memcpy(dst, src unsafe.Pointer, size uintptr) func memcpy(dst, src unsafe.Pointer, size uintptr)
//go:linkname alloc runtime.alloc //go:linkname alloc runtime.alloc

Просмотреть файл

@ -26,19 +26,19 @@ func GOROOT() string {
} }
// Copy size bytes from src to dst. The memory areas must not overlap. // Copy size bytes from src to dst. The memory areas must not overlap.
// Calls to this function are converted to LLVM intrinsic calls such as // This function is implemented by the compiler as a call to a LLVM intrinsic
// llvm.memcpy.p0i8.p0i8.i32(dst, src, size, false). // like llvm.memcpy.p0i8.p0i8.i32(dst, src, size, false).
func memcpy(dst, src unsafe.Pointer, size uintptr) func memcpy(dst, src unsafe.Pointer, size uintptr)
// Copy size bytes from src to dst. The memory areas may overlap and will do the // Copy size bytes from src to dst. The memory areas may overlap and will do the
// correct thing. // correct thing.
// Calls to this function are converted to LLVM intrinsic calls such as // This function is implemented by the compiler as a call to a LLVM intrinsic
// llvm.memmove.p0i8.p0i8.i32(dst, src, size, false). // like llvm.memmove.p0i8.p0i8.i32(dst, src, size, false).
func memmove(dst, src unsafe.Pointer, size uintptr) func memmove(dst, src unsafe.Pointer, size uintptr)
// Set the given number of bytes to zero. // Set the given number of bytes to zero.
// Calls to this function are converted to LLVM intrinsic calls such as // This function is implemented by the compiler as a call to a LLVM intrinsic
// llvm.memset.p0i8.i32(ptr, 0, size, false). // like llvm.memset.p0i8.i32(ptr, 0, size, false).
func memzero(ptr unsafe.Pointer, size uintptr) func memzero(ptr unsafe.Pointer, size uintptr)
// This intrinsic returns the current stack pointer. // This intrinsic returns the current stack pointer.