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  }