Merge pull request #327 from xushiwei/q

atomic Load/Store
This commit is contained in:
xushiwei
2024-06-16 16:47:19 +08:00
committed by GitHub
10 changed files with 223 additions and 156 deletions

View File

@@ -1,20 +1,24 @@
package main
import (
"github.com/goplus/llgo/c/sync"
"github.com/goplus/llgo/c/sync/atomic"
)
func main() {
var v int64 = 100
ret := sync.FetchAndAdd(&v, 1)
var v int64
atomic.Store(&v, 100)
println("store:", atomic.Load(&v))
ret := atomic.Add(&v, 1)
println("ret:", ret, "v:", v)
ret = sync.CompareAndXchg(&v, 100, 102)
ret = atomic.CompareAndExchange(&v, 100, 102)
println("ret:", ret, "vs 100, v:", v)
ret = sync.CompareAndXchg(&v, 101, 102)
ret = atomic.CompareAndExchange(&v, 101, 102)
println("ret:", ret, "vs 101, v:", v)
ret = sync.FetchAndSub(&v, 1)
ret = atomic.Sub(&v, 1)
println("ret:", ret, "v:", v)
}

71
c/sync/atomic/atomic.go Normal file
View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package atomic
import (
_ "unsafe"
)
const (
LLGoPackage = "decl"
)
type integer interface {
~int | ~uint | ~uintptr | ~int32 | ~uint32 | ~int64 | ~uint64
}
// llgo:link Add llgo.atomicAdd
func Add[T integer](ptr *T, v T) T { return v }
// llgo:link Sub llgo.atomicSub
func Sub[T integer](ptr *T, v T) T { return v }
// llgo:link And llgo.atomicAnd
func And[T integer](ptr *T, v T) T { return v }
// llgo:link NotAnd llgo.atomicNand
func NotAnd[T integer](ptr *T, v T) T { return v }
// llgo:link Or llgo.atomicOr
func Or[T integer](ptr *T, v T) T { return v }
// llgo:link Xor llgo.atomicXor
func Xor[T integer](ptr *T, v T) T { return v }
// llgo:link Max llgo.atomicMax
func Max[T integer](ptr *T, v T) T { return v }
// llgo:link Min llgo.atomicMin
func Min[T integer](ptr *T, v T) T { return v }
// llgo:link UMax llgo.atomicUMax
func UMax[T integer](ptr *T, v T) T { return v }
// llgo:link UMin llgo.atomicUMin
func UMin[T integer](ptr *T, v T) T { return v }
// llgo:link Load llgo.atomicLoad
func Load[T integer](ptr *T) T { return *ptr }
// llgo:link Store llgo.atomicStore
func Store[T integer](ptr *T, v T) {}
// llgo:link Exchange llgo.atomicXchg
func Exchange[T integer](ptr *T, v T) T { return v }
// llgo:link CompareAndExchange llgo.atomicCmpXchg
func CompareAndExchange[T integer](ptr *T, old, new T) T { return old }

View File

@@ -1,65 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sync
import (
_ "unsafe"
)
const (
LLGoPackage = "decl"
)
type integer interface {
~int | ~uint | ~uintptr | ~int32 | ~uint32 | ~int64 | ~uint64
}
// llgo:link FetchAndXchg llgo.atomicXchg
func FetchAndXchg[T integer](ptr *T, v T) T { return v }
// llgo:link FetchAndAdd llgo.atomicAdd
func FetchAndAdd[T integer](ptr *T, v T) T { return v }
// llgo:link FetchAndSub llgo.atomicSub
func FetchAndSub[T integer](ptr *T, v T) T { return v }
// llgo:link FetchAndAnd llgo.atomicAnd
func FetchAndAnd[T integer](ptr *T, v T) T { return v }
// llgo:link FetchAndNand llgo.atomicNand
func FetchAndNand[T integer](ptr *T, v T) T { return v }
// llgo:link FetchAndOr llgo.atomicOr
func FetchAndOr[T integer](ptr *T, v T) T { return v }
// llgo:link FetchAndXor llgo.atomicXor
func FetchAndXor[T integer](ptr *T, v T) T { return v }
// llgo:link FetchAndMax llgo.atomicMax
func FetchAndMax[T integer](ptr *T, v T) T { return v }
// llgo:link FetchAndMin llgo.atomicMin
func FetchAndMin[T integer](ptr *T, v T) T { return v }
// llgo:link FetchAndUMax llgo.atomicUMax
func FetchAndUMax[T integer](ptr *T, v T) T { return v }
// llgo:link FetchAndUMin llgo.atomicUMin
func FetchAndUMin[T integer](ptr *T, v T) T { return v }
// llgo:link CompareAndXchg llgo.atomicCmpXchg
func CompareAndXchg[T integer](ptr *T, old, new T) T { return old }

View File

@@ -2,20 +2,24 @@ package main
import (
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/sync"
"github.com/goplus/llgo/c/sync/atomic"
)
func main() {
var v int64 = 100
sync.FetchAndAdd(&v, 1)
c.Printf(c.Str("%ld\n"), v)
var v int64
sync.CompareAndXchg(&v, 100, 102)
c.Printf(c.Str("%ld\n"), v)
atomic.Store(&v, 100)
c.Printf(c.Str("store: %ld\n"), atomic.Load(&v))
sync.CompareAndXchg(&v, 101, 102)
c.Printf(c.Str("%ld\n"), v)
atomic.Add(&v, 1)
c.Printf(c.Str("v: %ld\n"), v)
sync.FetchAndSub(&v, 1)
c.Printf(c.Str("%ld\n"), v)
atomic.CompareAndExchange(&v, 100, 102)
c.Printf(c.Str("v: %ld\n"), v)
atomic.CompareAndExchange(&v, 101, 102)
c.Printf(c.Str("v: %ld\n"), v)
atomic.Sub(&v, 1)
c.Printf(c.Str("v: %ld\n"), v)
}

View File

@@ -4,10 +4,11 @@ source_filename = "main"
@"main.init$guard" = global i1 false, align 1
@__llgo_argc = global i32 0, align 4
@__llgo_argv = global ptr null, align 8
@0 = private unnamed_addr constant [5 x i8] c"%ld\0A\00", align 1
@1 = private unnamed_addr constant [5 x i8] c"%ld\0A\00", align 1
@2 = private unnamed_addr constant [5 x i8] c"%ld\0A\00", align 1
@3 = private unnamed_addr constant [5 x i8] c"%ld\0A\00", align 1
@0 = private unnamed_addr constant [12 x i8] c"store: %ld\0A\00", align 1
@1 = private unnamed_addr constant [8 x i8] c"v: %ld\0A\00", align 1
@2 = private unnamed_addr constant [8 x i8] c"v: %ld\0A\00", align 1
@3 = private unnamed_addr constant [8 x i8] c"v: %ld\0A\00", align 1
@4 = private unnamed_addr constant [8 x i8] c"v: %ld\0A\00", align 1
define void @main.init() {
_llgo_0:
@@ -29,19 +30,21 @@ _llgo_0:
call void @"github.com/goplus/llgo/internal/runtime.init"()
call void @main.init()
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8)
store i64 100, ptr %2, align 4
%3 = atomicrmw add ptr %2, i64 1 seq_cst, align 8
%4 = load i64, ptr %2, align 4
%5 = call i32 (ptr, ...) @printf(ptr @0, i64 %4)
%6 = cmpxchg ptr %2, i64 100, i64 102 seq_cst seq_cst, align 8
%7 = load i64, ptr %2, align 4
%8 = call i32 (ptr, ...) @printf(ptr @1, i64 %7)
%9 = cmpxchg ptr %2, i64 101, i64 102 seq_cst seq_cst, align 8
%10 = load i64, ptr %2, align 4
%11 = call i32 (ptr, ...) @printf(ptr @2, i64 %10)
%12 = atomicrmw sub ptr %2, i64 1 seq_cst, align 8
%13 = load i64, ptr %2, align 4
%14 = call i32 (ptr, ...) @printf(ptr @3, i64 %13)
store atomic i64 100, ptr %2 seq_cst, align 4
%3 = load atomic i64, ptr %2 seq_cst, align 4
%4 = call i32 (ptr, ...) @printf(ptr @0, i64 %3)
%5 = atomicrmw add ptr %2, i64 1 seq_cst, align 8
%6 = load i64, ptr %2, align 4
%7 = call i32 (ptr, ...) @printf(ptr @1, i64 %6)
%8 = cmpxchg ptr %2, i64 100, i64 102 seq_cst seq_cst, align 8
%9 = load i64, ptr %2, align 4
%10 = call i32 (ptr, ...) @printf(ptr @2, i64 %9)
%11 = cmpxchg ptr %2, i64 101, i64 102 seq_cst seq_cst, align 8
%12 = load i64, ptr %2, align 4
%13 = call i32 (ptr, ...) @printf(ptr @3, i64 %12)
%14 = atomicrmw sub ptr %2, i64 1 seq_cst, align 8
%15 = load i64, ptr %2, align 4
%16 = call i32 (ptr, ...) @printf(ptr @4, i64 %15)
ret i32 0
}

View File

@@ -140,6 +140,10 @@ func TestErrBuiltin(t *testing.T) {
test("siglongjmp", func(ctx *context) { ctx.siglongjmp(nil, nil) })
test("cstr(NoArgs)", func(ctx *context) { cstr(nil, nil) })
test("cstr(Nonconst)", func(ctx *context) { cstr(nil, []ssa.Value{&ssa.Parameter{}}) })
test("atomic", func(ctx *context) { ctx.atomic(nil, 0, nil) })
test("atomicLoad", func(ctx *context) { ctx.atomicLoad(nil, nil) })
test("atomicStore", func(ctx *context) { ctx.atomicStore(nil, nil) })
test("atomicCmpXchg", func(ctx *context) { ctx.atomicCmpXchg(nil, nil) })
}
func TestPkgNoInit(t *testing.T) {

View File

@@ -318,7 +318,10 @@ var llgoInstrs = map[string]int{
"deferData": llgoDeferData,
"unreachable": llgoUnreachable,
"atomicLoad": llgoAtomicLoad,
"atomicStore": llgoAtomicStore,
"atomicCmpXchg": llgoAtomicCmpXchg,
"atomicXchg": int(llgoAtomicXchg),
"atomicAdd": int(llgoAtomicAdd),
"atomicSub": int(llgoAtomicSub),
@@ -564,6 +567,24 @@ func (p *context) atomic(b llssa.Builder, op llssa.AtomicOp, args []ssa.Value) (
panic("atomicOp(addr *T, val T) T: invalid arguments")
}
func (p *context) atomicLoad(b llssa.Builder, args []ssa.Value) llssa.Expr {
if len(args) == 1 {
addr := p.compileValue(b, args[0])
return b.Load(addr).SetOrdering(llssa.OrderingSeqConsistent)
}
panic("atomicLoad(addr *T) T: invalid arguments")
}
func (p *context) atomicStore(b llssa.Builder, args []ssa.Value) {
if len(args) == 2 {
addr := p.compileValue(b, args[0])
val := p.compileValue(b, args[1])
b.Store(addr, val).SetOrdering(llssa.OrderingSeqConsistent)
return
}
panic("atomicStore(addr *T, val T) T: invalid arguments")
}
func (p *context) atomicCmpXchg(b llssa.Builder, args []ssa.Value) llssa.Expr {
if len(args) == 3 {
addr := p.compileValue(b, args[0])
@@ -677,6 +698,10 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon
ret = p.allocaCStr(b, args)
case llgoStringData:
ret = p.stringData(b, args)
case llgoAtomicLoad:
ret = p.atomicLoad(b, args)
case llgoAtomicStore:
p.atomicStore(b, args)
case llgoAtomicCmpXchg:
ret = p.atomicCmpXchg(b, args)
case llgoSigsetjmp:

View File

@@ -343,14 +343,18 @@ const (
llgoAdvance = llgoInstrBase + 4
llgoIndex = llgoInstrBase + 5
llgoStringData = llgoInstrBase + 6
llgoPyList = llgoInstrBase + 7
llgoDeferData = llgoInstrBase + 7
llgoSigjmpbuf = llgoInstrBase + 0xa
llgoSigsetjmp = llgoInstrBase + 0xb
llgoSiglongjmp = llgoInstrBase + 0xc
llgoDeferData = llgoInstrBase + 0xd
llgoAtomicCmpXchg = llgoInstrBase + 0xf
llgoAtomicOpBase = llgoInstrBase + 0x10
llgoPyList = llgoInstrBase + 0x10
llgoAtomicLoad = llgoInstrBase + 0x1d
llgoAtomicStore = llgoInstrBase + 0x1e
llgoAtomicCmpXchg = llgoInstrBase + 0x1f
llgoAtomicOpBase = llgoInstrBase + 0x20
llgoAtomicXchg = llgoAtomicOpBase + llssa.OpXchg
llgoAtomicAdd = llgoAtomicOpBase + llssa.OpAdd

View File

@@ -27,6 +27,18 @@ import (
"github.com/goplus/llvm"
)
type AtomicOrdering = llvm.AtomicOrdering
const (
OrderingNotAtomic = llvm.AtomicOrderingNotAtomic
OrderingUnordered = llvm.AtomicOrderingUnordered
OrderingMonotonic = llvm.AtomicOrderingMonotonic
OrderingAcquire = llvm.AtomicOrderingAcquire
OrderingRelease = llvm.AtomicOrderingRelease
OrderingAcquireRelease = llvm.AtomicOrderingAcquireRelease
OrderingSeqConsistent = llvm.AtomicOrderingSequentiallyConsistent
)
// -----------------------------------------------------------------------------
type Expr struct {
@@ -41,6 +53,12 @@ func (v Expr) IsNil() bool {
return v.Type == nil
}
// SetOrdering sets the ordering of the atomic operation.
func (v Expr) SetOrdering(ordering AtomicOrdering) Expr {
v.impl.SetOrdering(ordering)
return v
}
// -----------------------------------------------------------------------------
type builtinTy struct {

View File

@@ -26,46 +26,6 @@ import (
// -----------------------------------------------------------------------------
// Advance returns the pointer ptr advanced by offset.
func (b Builder) Advance(ptr Expr, offset Expr) Expr {
if debugInstr {
log.Printf("Advance %v, %v\n", ptr.impl, offset.impl)
}
var elem llvm.Type
var prog = b.Prog
switch t := ptr.raw.Type.(type) {
case *types.Basic: // void
elem = prog.tyInt8()
default:
elem = prog.rawType(t.(*types.Pointer).Elem()).ll
}
ret := llvm.CreateGEP(b.impl, elem, ptr.impl, []llvm.Value{offset.impl})
return Expr{ret, ptr.Type}
}
// Load returns the value at the pointer ptr.
func (b Builder) Load(ptr Expr) Expr {
if debugInstr {
log.Printf("Load %v\n", ptr.impl)
}
if ptr.kind == vkPyVarRef {
return b.pyLoad(ptr)
}
telem := b.Prog.Elem(ptr.Type)
return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem}
}
// Store stores val at the pointer ptr.
func (b Builder) Store(ptr, val Expr) Builder {
raw := ptr.raw.Type
if debugInstr {
log.Printf("Store %v, %v, %v\n", raw, ptr.impl, val.impl)
}
val = checkExpr(val, raw.(*types.Pointer).Elem(), b)
b.impl.CreateStore(val.impl, ptr.impl)
return b
}
func (b Builder) aggregateAllocU(t Type, flds ...llvm.Value) llvm.Value {
prog := b.Prog
size := prog.SizeOf(t)
@@ -295,4 +255,43 @@ func (b Builder) AtomicCmpXchg(ptr, old, new Expr) Expr {
return Expr{ret, t}
}
// Load returns the value at the pointer ptr.
func (b Builder) Load(ptr Expr) Expr {
if debugInstr {
log.Printf("Load %v\n", ptr.impl)
}
if ptr.kind == vkPyVarRef {
return b.pyLoad(ptr)
}
telem := b.Prog.Elem(ptr.Type)
return Expr{llvm.CreateLoad(b.impl, telem.ll, ptr.impl), telem}
}
// Store stores val at the pointer ptr.
func (b Builder) Store(ptr, val Expr) Expr {
raw := ptr.raw.Type
if debugInstr {
log.Printf("Store %v, %v, %v\n", raw, ptr.impl, val.impl)
}
val = checkExpr(val, raw.(*types.Pointer).Elem(), b)
return Expr{b.impl.CreateStore(val.impl, ptr.impl), b.Prog.Void()}
}
// Advance returns the pointer ptr advanced by offset.
func (b Builder) Advance(ptr Expr, offset Expr) Expr {
if debugInstr {
log.Printf("Advance %v, %v\n", ptr.impl, offset.impl)
}
var elem llvm.Type
var prog = b.Prog
switch t := ptr.raw.Type.(type) {
case *types.Basic: // void
elem = prog.tyInt8()
default:
elem = prog.rawType(t.(*types.Pointer).Elem()).ll
}
ret := llvm.CreateGEP(b.impl, elem, ptr.impl, []llvm.Value{offset.impl})
return Expr{ret, ptr.Type}
}
// -----------------------------------------------------------------------------