@ -4,19 +4,17 @@ import (
"fmt"
"strings"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
// createAtomicOp lowers an atomic library call by lowering it as an LLVM atomic
// operation. It returns the result of the operation and true if the call could
// be lowered inline, and false otherwise.
func ( b * builder ) createAtomicOp ( call * ssa . CallCommon ) ( llvm . Value , bool ) {
name := call . Value . ( * ssa . Function ) . Name ( )
// createAtomicOp lowers a sync/atomic function by lowering it as an LLVM atomic
// operation. It returns the result of the operation, or a zero llvm.Value if
// the result is void.
func ( b * builder ) createAtomicOp ( name string ) llvm . Value {
switch name {
case "AddInt32" , "AddInt64" , "AddUint32" , "AddUint64" , "AddUintptr" :
ptr := b . getValue ( call . Arg s[ 0 ] )
val := b . getValue ( call . Arg s[ 1 ] )
ptr := b . getValue ( b . fn . Param s[ 0 ] )
val := b . getValue ( b . fn . Param s[ 1 ] )
if strings . HasPrefix ( b . Triple , "avr" ) {
// AtomicRMW does not work on AVR as intended:
// - There are some register allocation issues (fixed by https://reviews.llvm.org/D97127 which is not yet in a usable LLVM release)
@ -29,17 +27,18 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
}
oldVal := b . createCall ( fn , [ ] llvm . Value { ptr , val } , "" )
// Return the new value, not the original value returned.
return b . CreateAdd ( oldVal , val , "" ) , true
return b . CreateAdd ( oldVal , val , "" )
}
oldVal := b . CreateAtomicRMW ( llvm . AtomicRMWBinOpAdd , ptr , val , llvm . AtomicOrderingSequentiallyConsistent , true )
// Return the new value, not the original value returned by atomicrmw.
return b . CreateAdd ( oldVal , val , "" ) , true
return b . CreateAdd ( oldVal , val , "" )
case "SwapInt32" , "SwapInt64" , "SwapUint32" , "SwapUint64" , "SwapUintptr" , "SwapPointer" :
ptr := b . getValue ( call . Arg s[ 0 ] )
val := b . getValue ( call . Arg s[ 1 ] )
ptr := b . getValue ( b . fn . Param s[ 0 ] )
val := b . getValue ( b . fn . Param s[ 1 ] )
isPointer := val . Type ( ) . TypeKind ( ) == llvm . PointerTypeKind
if isPointer {
// atomicrmw only supports integers, so cast to an integer.
// TODO: this is fixed in LLVM 15.
val = b . CreatePtrToInt ( val , b . uintptrType , "" )
ptr = b . CreateBitCast ( ptr , llvm . PointerType ( val . Type ( ) , 0 ) , "" )
}
@ -47,23 +46,23 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
if isPointer {
oldVal = b . CreateIntToPtr ( oldVal , b . i8ptrType , "" )
}
return oldVal , true
return oldVal
case "CompareAndSwapInt32" , "CompareAndSwapInt64" , "CompareAndSwapUint32" , "CompareAndSwapUint64" , "CompareAndSwapUintptr" , "CompareAndSwapPointer" :
ptr := b . getValue ( call . Arg s[ 0 ] )
old := b . getValue ( call . Arg s[ 1 ] )
newVal := b . getValue ( call . Arg s[ 2 ] )
ptr := b . getValue ( b . fn . Param s[ 0 ] )
old := b . getValue ( b . fn . Param s[ 1 ] )
newVal := b . getValue ( b . fn . Param s[ 2 ] )
tuple := b . CreateAtomicCmpXchg ( ptr , old , newVal , llvm . AtomicOrderingSequentiallyConsistent , llvm . AtomicOrderingSequentiallyConsistent , true )
swapped := b . CreateExtractValue ( tuple , 1 , "" )
return swapped , true
return swapped
case "LoadInt32" , "LoadInt64" , "LoadUint32" , "LoadUint64" , "LoadUintptr" , "LoadPointer" :
ptr := b . getValue ( call . Arg s[ 0 ] )
ptr := b . getValue ( b . fn . Param s[ 0 ] )
val := b . CreateLoad ( ptr , "" )
val . SetOrdering ( llvm . AtomicOrderingSequentiallyConsistent )
val . SetAlignment ( b . targetData . PrefTypeAlignment ( val . Type ( ) ) ) // required
return val , true
return val
case "StoreInt32" , "StoreInt64" , "StoreUint32" , "StoreUint64" , "StoreUintptr" , "StorePointer" :
ptr := b . getValue ( call . Arg s[ 0 ] )
val := b . getValue ( call . Arg s[ 1 ] )
ptr := b . getValue ( b . fn . Param s[ 0 ] )
val := b . getValue ( b . fn . Param s[ 1 ] )
if strings . HasPrefix ( b . Triple , "avr" ) {
// SelectionDAGBuilder is currently missing the "are unaligned atomics allowed" check for stores.
vType := val . Type ( )
@ -79,13 +78,15 @@ func (b *builder) createAtomicOp(call *ssa.CallCommon) (llvm.Value, bool) {
if fn . IsNil ( ) {
fn = llvm . AddFunction ( b . mod , name , llvm . FunctionType ( vType , [ ] llvm . Type { ptr . Type ( ) , vType , b . uintptrType } , false ) )
}
return b . createCall ( fn , [ ] llvm . Value { ptr , val , llvm . ConstInt ( b . uintptrType , 5 , false ) } , "" ) , true
b . createCall ( fn , [ ] llvm . Value { ptr , val , llvm . ConstInt ( b . uintptrType , 5 , false ) } , "" )
return llvm . Value { }
}
store := b . CreateStore ( val , ptr )
store . SetOrdering ( llvm . AtomicOrderingSequentiallyConsistent )
store . SetAlignment ( b . targetData . PrefTypeAlignment ( val . Type ( ) ) ) // required
return store , true
return llvm . Value { }
default :
return llvm . Value { } , false
b . addError ( b . fn . Pos ( ) , "unknown atomic operation: " + b . fn . Name ( ) )
return llvm . Value { }
}
}