aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2020-03-26 16:09:48 +0100
committerRon Evans <[email protected]>2020-03-27 21:01:59 +0100
commit91d1a23b14fd95fb8bdcf306007a2c47d87c32a7 (patch)
tree3fe8fabd7ff5b9df82e9add99e3703bdc3727390
parenteaa54bc7e321f5fc1ae39c1b670c741f2b7d96de (diff)
downloadtinygo-91d1a23b14fd95fb8bdcf306007a2c47d87c32a7.tar.gz
tinygo-91d1a23b14fd95fb8bdcf306007a2c47d87c32a7.zip
compiler,runtime: translate memzero calls to LLVM memset intrinsics
This gives the optimizer a bit more information about what the calls do. This should result in slightly better generated code. Code size sometimes goes up and sometimes goes down. I blame the code size going up on the inliner which inlines more functions, because compiling the smoke tests in the drivers repository with -opt=1 results in a slight code size reduction in all cases.
-rw-r--r--compiler/compiler.go2
-rw-r--r--compiler/intrinsics.go20
-rw-r--r--src/runtime/runtime.go8
3 files changed, 25 insertions, 5 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go
index 561095b03..d7609d13f 100644
--- a/compiler/compiler.go
+++ b/compiler/compiler.go
@@ -1343,6 +1343,8 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
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 == "device/arm.ReadRegister" || name == "device/riscv.ReadRegister":
return b.createReadRegister(name, instr.Args)
case name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
diff --git a/compiler/intrinsics.go b/compiler/intrinsics.go
index 4790b70d8..634e8362e 100644
--- a/compiler/intrinsics.go
+++ b/compiler/intrinsics.go
@@ -28,3 +28,23 @@ func (b *builder) createMemoryCopyCall(fn *ssa.Function, args []ssa.Value) (llvm
b.CreateCall(llvmFn, params, "")
return llvm.Value{}, nil
}
+
+// createMemoryZeroCall creates calls to llvm.memset.* to zero a block of
+// 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.
+func (b *builder) createMemoryZeroCall(args []ssa.Value) (llvm.Value, error) {
+ fnName := "llvm.memset.p0i8.i" + strconv.Itoa(b.uintptrType.IntTypeWidth())
+ llvmFn := b.mod.NamedFunction(fnName)
+ if llvmFn.IsNil() {
+ fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.i8ptrType, b.ctx.Int8Type(), b.uintptrType, b.ctx.Int1Type()}, false)
+ llvmFn = llvm.AddFunction(b.mod, fnName, fnType)
+ }
+ params := []llvm.Value{
+ b.getValue(args[0]),
+ llvm.ConstInt(b.ctx.Int8Type(), 0, false),
+ b.getValue(args[1]),
+ llvm.ConstInt(b.ctx.Int1Type(), 0, false),
+ }
+ b.CreateCall(llvmFn, params, "")
+ return llvm.Value{}, nil
+}
diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go
index d545e1513..61d21029e 100644
--- a/src/runtime/runtime.go
+++ b/src/runtime/runtime.go
@@ -41,11 +41,9 @@ func memcpy(dst, src unsafe.Pointer, size uintptr)
func memmove(dst, src unsafe.Pointer, size uintptr)
// Set the given number of bytes to zero.
-func memzero(ptr unsafe.Pointer, size uintptr) {
- for i := uintptr(0); i < size; i++ {
- *(*byte)(unsafe.Pointer(uintptr(ptr) + i)) = 0
- }
-}
+// Calls to this function are converted to LLVM intrinsic calls such as
+// llvm.memset.p0i8.i32(ptr, 0, size, false).
+func memzero(ptr unsafe.Pointer, size uintptr)
// Compare two same-size buffers for equality.
func memequal(x, y unsafe.Pointer, n uintptr) bool {