diff --git a/compiler/compiler.go b/compiler/compiler.go index db5d442c..a31c4d7d 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -195,7 +195,7 @@ func (c *Compiler) Compile(mainPath string) []error { }, ShouldOverlay: func(path string) bool { switch path { - case "machine", "os", "reflect", "runtime", "sync": + case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync": return true default: if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") { @@ -1265,17 +1265,22 @@ 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, "runtime/volatile.Load"): + return c.emitVolatileLoad(frame, instr) + case strings.HasPrefix(name, "runtime/volatile.Store"): + return c.emitVolatileStore(frame, instr) } targetFunc := c.ir.GetFunction(fn) diff --git a/compiler/volatile.go b/compiler/volatile.go new file mode 100644 index 00000000..7ee5b168 --- /dev/null +++ b/compiler/volatile.go @@ -0,0 +1,26 @@ +package compiler + +// This file implements volatile loads/stores in runtime/volatile.LoadT and +// runtime/volatile.StoreT as compiler builtins. + +import ( + "golang.org/x/tools/go/ssa" + "tinygo.org/x/go-llvm" +) + +func (c *Compiler) emitVolatileLoad(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { + addr := c.getValue(frame, instr.Args[0]) + c.emitNilCheck(frame, addr, "deref") + val := c.builder.CreateLoad(addr, "") + val.SetVolatile(true) + return val, nil +} + +func (c *Compiler) emitVolatileStore(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) { + addr := c.getValue(frame, instr.Args[0]) + val := c.getValue(frame, instr.Args[1]) + c.emitNilCheck(frame, addr, "deref") + store := c.builder.CreateStore(val, addr) + store.SetVolatile(true) + return llvm.Value{}, nil +} diff --git a/src/runtime/volatile/volatile.go b/src/runtime/volatile/volatile.go new file mode 100644 index 00000000..47262f34 --- /dev/null +++ b/src/runtime/volatile/volatile.go @@ -0,0 +1,34 @@ +// Package volatile provides definitions for volatile loads and stores. These +// are implemented as compiler builtins. +// +// The load operations load a volatile value. The store operations store to a +// volatile value. The compiler will emit exactly one load or store operation +// when possible and will not reorder volatile operations. However, the compiler +// may move other operations across load/store operations, so make sure that all +// relevant loads/stores are done in a volatile way if this is a problem. +// +// These loads and stores are commonly used to read/write values from memory +// mapped peripheral devices. They do not provide atomicity, use the sync/atomic +// package for that. +// +// For more details: https://llvm.org/docs/LangRef.html#volatile-memory-accesses +// and https://blog.regehr.org/archives/28. +package volatile + +// LoadUint8 loads the volatile value *addr. +func LoadUint8(addr *uint8) (val uint8) + +// LoadUint16 loads the volatile value *addr. +func LoadUint16(addr *uint16) (val uint16) + +// LoadUint32 loads the volatile value *addr. +func LoadUint32(addr *uint32) (val uint32) + +// StoreUint8 stores val to the volatile value *addr. +func StoreUint8(addr *uint8, val uint8) + +// StoreUint16 stores val to the volatile value *addr. +func StoreUint16(addr *uint16, val uint16) + +// StoreUint32 stores val to the volatile value *addr. +func StoreUint32(addr *uint32, val uint32)