Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ var genericBuiltins = []string{
"umodti3.c",
}

var aeabiBuiltins = []string{
var armBuiltins = []string{
"arm/aeabi_cdcmp.S",
"arm/aeabi_cdcmpeq_check_nan.c",
"arm/aeabi_cfcmp.S",
Expand All @@ -158,12 +158,24 @@ var aeabiBuiltins = []string{
"arm/aeabi_memset.S",
"arm/aeabi_uidivmod.S",
"arm/aeabi_uldivmod.S",
"arm/sync_fetch_and_add_4.S",
"arm/sync_fetch_and_add_8.S",
"arm/sync_fetch_and_and_4.S",
"arm/sync_fetch_and_and_8.S",
"arm/sync_fetch_and_nand_4.S",
"arm/sync_fetch_and_nand_8.S",
"arm/sync_fetch_and_or_4.S",
"arm/sync_fetch_and_or_8.S",
"arm/sync_fetch_and_sub_4.S",
"arm/sync_fetch_and_sub_8.S",
"arm/sync_fetch_and_xor_4.S",
"arm/sync_fetch_and_xor_8.S",
}

func builtinFiles(target string) []string {
builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins
if strings.HasPrefix(target, "arm") {
builtins = append(builtins, aeabiBuiltins...)
builtins = append(builtins, armBuiltins...)
}
return builtins
}
Expand Down
114 changes: 114 additions & 0 deletions compiler/atomic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package compiler

import (
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)

func (c *Compiler) emitAtomic(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
name := call.Value.(*ssa.Function).Name()
switch name {
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
ptr, err := c.parseExpr(frame, call.Args[0])
if err != nil {
return llvm.Value{}, err
}
val, err := c.parseExpr(frame, call.Args[1])
if err != nil {
return llvm.Value{}, err
}
if c.GOARCH == "wasm" {
ptrVal := c.builder.CreateLoad(ptr, "")
ptrVal = c.builder.CreateAdd(ptrVal, val, "")
c.builder.CreateStore(ptrVal, ptr)
return ptrVal, nil
}
oldVal := c.builder.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
// Return the new value, not the original value returned by atomicrmw.
return c.builder.CreateAdd(oldVal, val, ""), nil
case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer":
ptr, err := c.parseExpr(frame, call.Args[0])
if err != nil {
return llvm.Value{}, err
}
val, err := c.parseExpr(frame, call.Args[1])
if err != nil {
return llvm.Value{}, err
}
if c.GOARCH == "wasm" {
old := c.builder.CreateLoad(ptr, "")
c.builder.CreateStore(val, ptr)
return old, nil
}
isPointer := val.Type().TypeKind() == llvm.PointerTypeKind
if isPointer {
// atomicrmw only supports integers, so cast to an integer.
val = c.builder.CreatePtrToInt(val, c.uintptrType, "")
ptr = c.builder.CreateBitCast(ptr, llvm.PointerType(val.Type(), 0), "")
}
oldVal := c.builder.CreateAtomicRMW(llvm.AtomicRMWBinOpXchg, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true)
if isPointer {
oldVal = c.builder.CreateIntToPtr(oldVal, c.i8ptrType, "")
}
return oldVal, nil
case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer":
ptr, err := c.parseExpr(frame, call.Args[0])
if err != nil {
return llvm.Value{}, err
}
old, err := c.parseExpr(frame, call.Args[1])
if err != nil {
return llvm.Value{}, err
}
newVal, err := c.parseExpr(frame, call.Args[2])
if err != nil {
return llvm.Value{}, err
}
if c.GOARCH == "wasm" {
swapBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "cas.swap")
nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "cas.next")
frame.blockExits[frame.currentBlock] = nextBlock
val := c.builder.CreateLoad(ptr, "")
swapped := c.builder.CreateICmp(llvm.IntEQ, val, old, "")
c.builder.CreateCondBr(swapped, swapBlock, nextBlock)
c.builder.SetInsertPointAtEnd(swapBlock)
c.builder.CreateStore(newVal, ptr)
c.builder.CreateBr(nextBlock)
c.builder.SetInsertPointAtEnd(nextBlock)
return swapped, nil
}
tuple := c.builder.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true)
swapped := c.builder.CreateExtractValue(tuple, 1, "")
return swapped, nil
case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer":
ptr, err := c.parseExpr(frame, call.Args[0])
if err != nil {
return llvm.Value{}, err
}
val := c.builder.CreateLoad(ptr, "")
if c.GOARCH == "wasm" {
return val, nil
}
val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
val.SetAlignment(c.targetData.PrefTypeAlignment(val.Type())) // required
return val, nil
case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer":
ptr, err := c.parseExpr(frame, call.Args[0])
if err != nil {
return llvm.Value{}, err
}
val, err := c.parseExpr(frame, call.Args[1])
if err != nil {
return llvm.Value{}, err
}
store := c.builder.CreateStore(val, ptr)
if c.GOARCH == "wasm" {
return store, nil
}
store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent)
store.SetAlignment(c.targetData.PrefTypeAlignment(val.Type())) // required
return store, nil
default:
return llvm.Value{}, c.makeError(call.Pos(), "unknown atomic call: "+name)
}
}
15 changes: 9 additions & 6 deletions compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1291,17 +1291,20 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e

// Try to call the function directly for trivially static calls.
if fn := instr.StaticCallee(); fn != nil {
switch fn.RelString(nil) {
case "device/arm.ReadRegister":
name := fn.RelString(nil)
switch {
case name == "device/arm.ReadRegister":
return c.emitReadRegister(instr.Args)
case "device/arm.Asm", "device/avr.Asm":
case name == "device/arm.Asm" || name == "device/avr.Asm":
return c.emitAsm(instr.Args)
case "device/arm.AsmFull", "device/avr.AsmFull":
case name == "device/arm.AsmFull" || name == "device/avr.AsmFull":
return c.emitAsmFull(frame, instr)
case "device/arm.SVCall0", "device/arm.SVCall1", "device/arm.SVCall2", "device/arm.SVCall3", "device/arm.SVCall4":
case strings.HasPrefix(name, "device/arm.SVCall"):
return c.emitSVCall(frame, instr.Args)
case "syscall.Syscall", "syscall.Syscall6", "syscall.Syscall9":
case strings.HasPrefix(name, "syscall.Syscall"):
return c.emitSyscall(frame, instr)
case strings.HasPrefix(name, "sync/atomic."):
return c.emitAtomic(frame, instr)
}

targetFunc := c.ir.GetFunction(fn)
Expand Down
3 changes: 3 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ func TestCompiler(t *testing.T) {

t.Log("running tests for emulated cortex-m3...")
for _, path := range matches {
if path == "testdata/atomic.go" {
continue // TODO: add builtins to compiler-rt
}
t.Run(path, func(t *testing.T) {
runTest(path, tmpdir, "qemu", t)
})
Expand Down
80 changes: 80 additions & 0 deletions testdata/atomic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package main

import (
"sync/atomic"
"unsafe"
)

func main() {
i32 := int32(-5)
println("AddInt32:", atomic.AddInt32(&i32, 8), i32)

i64 := int64(-5)
println("AddInt64:", atomic.AddInt64(&i64, 8), i64)

u32 := uint32(5)
println("AddUint32:", atomic.AddUint32(&u32, 8), u32)

u64 := uint64(5)
println("AddUint64:", atomic.AddUint64(&u64, 8), u64)

uptr := uintptr(5)
println("AddUintptr:", uint64(atomic.AddUintptr(&uptr, 8)), uint64(uptr))

println("SwapInt32:", atomic.SwapInt32(&i32, 33), i32)
println("SwapInt64:", atomic.SwapInt64(&i64, 33), i64)
println("SwapUint32:", atomic.SwapUint32(&u32, 33), u32)
println("SwapUint64:", atomic.SwapUint64(&u64, 33), u64)
println("SwapUintptr:", uint64(atomic.SwapUintptr(&uptr, 33)), uint64(uptr))
ptr := unsafe.Pointer(&i32)
println("SwapPointer:", atomic.SwapPointer(&ptr, unsafe.Pointer(&u32)) == unsafe.Pointer(&i32), ptr == unsafe.Pointer(&u32))

i32 = int32(-5)
println("CompareAndSwapInt32:", atomic.CompareAndSwapInt32(&i32, 5, 3), i32)
println("CompareAndSwapInt32:", atomic.CompareAndSwapInt32(&i32, -5, 3), i32)

i64 = int64(-5)
println("CompareAndSwapInt64:", atomic.CompareAndSwapInt64(&i64, 5, 3), i64)
println("CompareAndSwapInt64:", atomic.CompareAndSwapInt64(&i64, -5, 3), i64)

u32 = uint32(5)
println("CompareAndSwapUint32:", atomic.CompareAndSwapUint32(&u32, 4, 3), u32)
println("CompareAndSwapUint32:", atomic.CompareAndSwapUint32(&u32, 5, 3), u32)

u64 = uint64(5)
println("CompareAndSwapUint64:", atomic.CompareAndSwapUint64(&u64, 4, 3), u64)
println("CompareAndSwapUint64:", atomic.CompareAndSwapUint64(&u64, 5, 3), u64)

uptr = uintptr(5)
println("CompareAndSwapUintptr:", atomic.CompareAndSwapUintptr(&uptr, 4, 3), uint64(uptr))
println("CompareAndSwapUintptr:", atomic.CompareAndSwapUintptr(&uptr, 5, 3), uint64(uptr))

ptr = unsafe.Pointer(&i32)
println("CompareAndSwapPointer:", atomic.CompareAndSwapPointer(&ptr, unsafe.Pointer(&u32), unsafe.Pointer(&i64)), ptr == unsafe.Pointer(&i32))
println("CompareAndSwapPointer:", atomic.CompareAndSwapPointer(&ptr, unsafe.Pointer(&i32), unsafe.Pointer(&i64)), ptr == unsafe.Pointer(&i64))

println("LoadInt32:", atomic.LoadInt32(&i32))
println("LoadInt64:", atomic.LoadInt64(&i64))
println("LoadUint32:", atomic.LoadUint32(&u32))
println("LoadUint64:", atomic.LoadUint64(&u64))
println("LoadUintptr:", uint64(atomic.LoadUintptr(&uptr)))
println("LoadPointer:", atomic.LoadPointer(&ptr) == unsafe.Pointer(&i64))

atomic.StoreInt32(&i32, -20)
println("StoreInt32:", i32)

atomic.StoreInt64(&i64, -20)
println("StoreInt64:", i64)

atomic.StoreUint32(&u32, 20)
println("StoreUint32:", u32)

atomic.StoreUint64(&u64, 20)
println("StoreUint64:", u64)

atomic.StoreUintptr(&uptr, 20)
println("StoreUintptr:", uint64(uptr))

atomic.StorePointer(&ptr, unsafe.Pointer(&uptr))
println("StorePointer:", ptr == unsafe.Pointer(&uptr))
}
35 changes: 35 additions & 0 deletions testdata/atomic.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
AddInt32: 3 3
AddInt64: 3 3
AddUint32: 13 13
AddUint64: 13 13
AddUintptr: 13 13
SwapInt32: 3 33
SwapInt64: 3 33
SwapUint32: 13 33
SwapUint64: 13 33
SwapUintptr: 13 33
SwapPointer: true true
CompareAndSwapInt32: false -5
CompareAndSwapInt32: true 3
CompareAndSwapInt64: false -5
CompareAndSwapInt64: true 3
CompareAndSwapUint32: false 5
CompareAndSwapUint32: true 3
CompareAndSwapUint64: false 5
CompareAndSwapUint64: true 3
CompareAndSwapUintptr: false 5
CompareAndSwapUintptr: true 3
CompareAndSwapPointer: false true
CompareAndSwapPointer: true true
LoadInt32: 3
LoadInt64: 3
LoadUint32: 3
LoadUint64: 3
LoadUintptr: 3
LoadPointer: true
StoreInt32: -20
StoreInt64: -20
StoreUint32: 20
StoreUint64: 20
StoreUintptr: 20
StorePointer: true