github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/compiler/atomic.go (about) 1 package compiler 2 3 import ( 4 "fmt" 5 "strings" 6 7 "tinygo.org/x/go-llvm" 8 ) 9 10 // createAtomicOp lowers a sync/atomic function by lowering it as an LLVM atomic 11 // operation. It returns the result of the operation, or a zero llvm.Value if 12 // the result is void. 13 func (b *builder) createAtomicOp(name string) llvm.Value { 14 switch name { 15 case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr": 16 ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) 17 val := b.getValue(b.fn.Params[1], getPos(b.fn)) 18 if strings.HasPrefix(b.Triple, "avr") { 19 // AtomicRMW does not work on AVR as intended: 20 // - There are some register allocation issues (fixed by https://reviews.llvm.org/D97127 which is not yet in a usable LLVM release) 21 // - The result is the new value instead of the old value 22 vType := val.Type() 23 name := fmt.Sprintf("__sync_fetch_and_add_%d", vType.IntTypeWidth()/8) 24 fn := b.mod.NamedFunction(name) 25 if fn.IsNil() { 26 fn = llvm.AddFunction(b.mod, name, llvm.FunctionType(vType, []llvm.Type{ptr.Type(), vType}, false)) 27 } 28 oldVal := b.createCall(fn.GlobalValueType(), fn, []llvm.Value{ptr, val}, "") 29 // Return the new value, not the original value returned. 30 return b.CreateAdd(oldVal, val, "") 31 } 32 oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpAdd, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true) 33 // Return the new value, not the original value returned by atomicrmw. 34 return b.CreateAdd(oldVal, val, "") 35 case "SwapInt32", "SwapInt64", "SwapUint32", "SwapUint64", "SwapUintptr", "SwapPointer": 36 ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) 37 val := b.getValue(b.fn.Params[1], getPos(b.fn)) 38 oldVal := b.CreateAtomicRMW(llvm.AtomicRMWBinOpXchg, ptr, val, llvm.AtomicOrderingSequentiallyConsistent, true) 39 return oldVal 40 case "CompareAndSwapInt32", "CompareAndSwapInt64", "CompareAndSwapUint32", "CompareAndSwapUint64", "CompareAndSwapUintptr", "CompareAndSwapPointer": 41 ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) 42 old := b.getValue(b.fn.Params[1], getPos(b.fn)) 43 newVal := b.getValue(b.fn.Params[2], getPos(b.fn)) 44 tuple := b.CreateAtomicCmpXchg(ptr, old, newVal, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, true) 45 swapped := b.CreateExtractValue(tuple, 1, "") 46 return swapped 47 case "LoadInt32", "LoadInt64", "LoadUint32", "LoadUint64", "LoadUintptr", "LoadPointer": 48 ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) 49 val := b.CreateLoad(b.getLLVMType(b.fn.Signature.Results().At(0).Type()), ptr, "") 50 val.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent) 51 val.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required 52 return val 53 case "StoreInt32", "StoreInt64", "StoreUint32", "StoreUint64", "StoreUintptr", "StorePointer": 54 ptr := b.getValue(b.fn.Params[0], getPos(b.fn)) 55 val := b.getValue(b.fn.Params[1], getPos(b.fn)) 56 store := b.CreateStore(val, ptr) 57 store.SetOrdering(llvm.AtomicOrderingSequentiallyConsistent) 58 store.SetAlignment(b.targetData.PrefTypeAlignment(val.Type())) // required 59 return llvm.Value{} 60 default: 61 b.addError(b.fn.Pos(), "unknown atomic operation: "+b.fn.Name()) 62 return llvm.Value{} 63 } 64 }