diff --git a/_demo/catomic/atomic.go b/_demo/catomic/atomic.go index 9125f42d..c4688aa7 100644 --- a/_demo/catomic/atomic.go +++ b/_demo/catomic/atomic.go @@ -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) } diff --git a/c/sync/atomic/atomic.go b/c/sync/atomic/atomic.go new file mode 100644 index 00000000..d7548a8d --- /dev/null +++ b/c/sync/atomic/atomic.go @@ -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 } diff --git a/c/sync/sync.go b/c/sync/sync.go deleted file mode 100644 index e79859bd..00000000 --- a/c/sync/sync.go +++ /dev/null @@ -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 } diff --git a/cl/_testlibc/atomic/in.go b/cl/_testlibc/atomic/in.go index 8c05f87a..ed847a2d 100644 --- a/cl/_testlibc/atomic/in.go +++ b/cl/_testlibc/atomic/in.go @@ -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) } diff --git a/cl/_testlibc/atomic/out.ll b/cl/_testlibc/atomic/out.ll index d0680e88..80b97edd 100644 --- a/cl/_testlibc/atomic/out.ll +++ b/cl/_testlibc/atomic/out.ll @@ -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 } diff --git a/cl/builtin_test.go b/cl/builtin_test.go index 08b95e87..9d3ee83d 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -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) { diff --git a/cl/compile.go b/cl/compile.go index b3d83aa8..3b631e69 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -318,18 +318,21 @@ var llgoInstrs = map[string]int{ "deferData": llgoDeferData, "unreachable": llgoUnreachable, + "atomicLoad": llgoAtomicLoad, + "atomicStore": llgoAtomicStore, "atomicCmpXchg": llgoAtomicCmpXchg, - "atomicXchg": int(llgoAtomicXchg), - "atomicAdd": int(llgoAtomicAdd), - "atomicSub": int(llgoAtomicSub), - "atomicAnd": int(llgoAtomicAnd), - "atomicNand": int(llgoAtomicNand), - "atomicOr": int(llgoAtomicOr), - "atomicXor": int(llgoAtomicXor), - "atomicMax": int(llgoAtomicMax), - "atomicMin": int(llgoAtomicMin), - "atomicUMax": int(llgoAtomicUMax), - "atomicUMin": int(llgoAtomicUMin), + + "atomicXchg": int(llgoAtomicXchg), + "atomicAdd": int(llgoAtomicAdd), + "atomicSub": int(llgoAtomicSub), + "atomicAnd": int(llgoAtomicAnd), + "atomicNand": int(llgoAtomicNand), + "atomicOr": int(llgoAtomicOr), + "atomicXor": int(llgoAtomicXor), + "atomicMax": int(llgoAtomicMax), + "atomicMin": int(llgoAtomicMin), + "atomicUMax": int(llgoAtomicUMax), + "atomicUMin": int(llgoAtomicUMin), } // funcOf returns a function by name and set ftype = goFunc, cFunc, etc. @@ -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: diff --git a/cl/import.go b/cl/import.go index 8f2a6f98..c1217778 100644 --- a/cl/import.go +++ b/cl/import.go @@ -343,14 +343,18 @@ const ( llgoAdvance = llgoInstrBase + 4 llgoIndex = llgoInstrBase + 5 llgoStringData = llgoInstrBase + 6 - llgoPyList = llgoInstrBase + 7 - llgoSigjmpbuf = llgoInstrBase + 0xa - llgoSigsetjmp = llgoInstrBase + 0xb - llgoSiglongjmp = llgoInstrBase + 0xc - llgoDeferData = llgoInstrBase + 0xd + llgoDeferData = llgoInstrBase + 7 - llgoAtomicCmpXchg = llgoInstrBase + 0xf - llgoAtomicOpBase = llgoInstrBase + 0x10 + llgoSigjmpbuf = llgoInstrBase + 0xa + llgoSigsetjmp = llgoInstrBase + 0xb + llgoSiglongjmp = llgoInstrBase + 0xc + + llgoPyList = llgoInstrBase + 0x10 + + llgoAtomicLoad = llgoInstrBase + 0x1d + llgoAtomicStore = llgoInstrBase + 0x1e + llgoAtomicCmpXchg = llgoInstrBase + 0x1f + llgoAtomicOpBase = llgoInstrBase + 0x20 llgoAtomicXchg = llgoAtomicOpBase + llssa.OpXchg llgoAtomicAdd = llgoAtomicOpBase + llssa.OpAdd diff --git a/ssa/expr.go b/ssa/expr.go index 6b58f08a..b6912a34 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -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 { diff --git a/ssa/memory.go b/ssa/memory.go index 1baaf303..244122ee 100644 --- a/ssa/memory.go +++ b/ssa/memory.go @@ -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} +} + // -----------------------------------------------------------------------------