/* * 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 ssa import ( "go/token" "go/types" "log" "github.com/goplus/llvm" ) // ----------------------------------------------------------------------------- func (b Builder) aggregateAllocU(t Type, flds ...llvm.Value) llvm.Value { prog := b.Prog size := prog.SizeOf(t) ptr := b.allocUninited(prog.IntVal(size, prog.Uintptr())).impl aggregateInit(b.impl, ptr, t.ll, flds...) return ptr } func (b Builder) aggregateAlloca(t Type, flds ...llvm.Value) llvm.Value { prog := b.Prog size := prog.SizeOf(t) ptr := b.Alloca(prog.IntVal(size, prog.Uintptr())).impl aggregateInit(b.impl, ptr, t.ll, flds...) return ptr } func (b Builder) aggregateMalloc(t Type, flds ...llvm.Value) llvm.Value { prog := b.Prog size := prog.SizeOf(t) ptr := b.malloc(prog.IntVal(size, prog.Uintptr())).impl aggregateInit(b.impl, ptr, t.ll, flds...) return ptr } // aggregateValue yields the value of the aggregate X with the fields func (b Builder) aggregateValue(t Type, flds ...llvm.Value) Expr { return Expr{aggregateValue(b.impl, t.ll, flds...), t} } func aggregateValue(b llvm.Builder, tll llvm.Type, flds ...llvm.Value) llvm.Value { ptr := llvm.CreateAlloca(b, tll) aggregateInit(b, ptr, tll, flds...) return llvm.CreateLoad(b, tll, ptr) } func aggregateInit(b llvm.Builder, ptr llvm.Value, tll llvm.Type, flds ...llvm.Value) { for i, fld := range flds { b.CreateStore(fld, llvm.CreateStructGEP(b, tll, ptr, i)) } } /* func (b Builder) dupMalloc(v Expr) Expr { prog := b.Prog n := prog.SizeOf(v.Type) tptr := prog.Pointer(v.Type) ptr := b.malloc(prog.Val(uintptr(n))).impl b.Store(Expr{ptr, tptr}, v) return Expr{ptr, tptr} } */ // ----------------------------------------------------------------------------- // The Alloc instruction reserves space for a variable of the given type, // zero-initializes it, and yields its address. // // If heap is false, Alloc zero-initializes the same local variable in // the call frame and returns its address; in this case the Alloc must // be present in Function.Locals. We call this a "local" alloc. // // If heap is true, Alloc allocates a new zero-initialized variable // each time the instruction is executed. We call this a "new" alloc. // // When Alloc is applied to a channel, map or slice type, it returns // the address of an uninitialized (nil) reference of that kind; store // the result of MakeSlice, MakeMap or MakeChan in that location to // instantiate these types. // // Example printed form: // // t0 = local int // t1 = new int func (b Builder) Alloc(elem Type, heap bool) (ret Expr) { if debugInstr { log.Printf("Alloc %v, %v\n", elem.RawType(), heap) } prog := b.Prog pkg := b.Pkg size := SizeOf(prog, elem) if heap { ret = b.InlineCall(pkg.rtFunc("AllocZ"), size) } else { ret = Expr{llvm.CreateAlloca(b.impl, elem.ll), prog.VoidPtr()} ret.impl = b.InlineCall(pkg.rtFunc("Zeroinit"), ret, size).impl } ret.Type = prog.Pointer(elem) return } // AllocU allocates uninitialized space for n*sizeof(elem) bytes. func (b Builder) AllocU(elem Type, n ...int64) (ret Expr) { prog := b.Prog size := SizeOf(prog, elem, n...) return Expr{b.allocUninited(size).impl, prog.Pointer(elem)} } func (b Builder) allocUninited(size Expr) (ret Expr) { return b.InlineCall(b.Pkg.rtFunc("AllocU"), size) } // AllocZ allocates zero initialized space for n bytes. func (b Builder) AllocZ(n Expr) (ret Expr) { return b.InlineCall(b.Pkg.rtFunc("AllocZ"), n) } // Alloca allocates uninitialized space for n bytes. func (b Builder) Alloca(n Expr) (ret Expr) { if debugInstr { log.Printf("Alloca %v\n", n.impl) } prog := b.Prog telem := prog.tyInt8() ret.impl = llvm.CreateArrayAlloca(b.impl, telem, n.impl) ret.Type = prog.VoidPtr() return } /* TODO(xsw): // AllocaU allocates uninitialized space for n*sizeof(elem) bytes. func (b Builder) AllocaU(elem Type, n ...int64) (ret Expr) { prog := b.Prog size := SizeOf(prog, elem, n...) return Expr{b.Alloca(size).impl, prog.Pointer(elem)} } */ // AllocaCStr allocates space for copy it from a Go string. func (b Builder) AllocaCStr(gostr Expr) (ret Expr) { if debugInstr { log.Printf("AllocaCStr %v\n", gostr.impl) } n := b.StringLen(gostr) n1 := b.BinOp(token.ADD, n, b.Prog.Val(1)) cstr := b.Alloca(n1) return b.InlineCall(b.Pkg.rtFunc("CStrCopy"), cstr, gostr) } // func allocaCStrs(strs []string, endWithNil bool) **int8 func (b Builder) AllocaCStrs(strs Expr, endWithNil bool) (cstrs Expr) { if debugInstr { log.Printf("AllocaCStrs %v, %v\n", strs.impl, endWithNil) } prog := b.Prog n := b.SliceLen(strs) n1 := n if endWithNil { n1 = b.BinOp(token.ADD, n, prog.Val(1)) } tcstr := prog.CStr() cstrs = b.ArrayAlloca(tcstr, n1) b.Times(n, func(i Expr) { pstr := b.IndexAddr(strs, i) s := b.Load(pstr) b.Store(b.Advance(cstrs, i), b.AllocaCStr(s)) }) if endWithNil { b.Store(b.Advance(cstrs, n), prog.Nil(tcstr)) } return } // ----------------------------------------------------------------------------- func (p Program) tyMalloc() *types.Signature { if p.mallocTy == nil { paramSize := types.NewParam(token.NoPos, nil, "", p.Uintptr().raw.Type) paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type) params := types.NewTuple(paramSize) results := types.NewTuple(paramPtr) p.mallocTy = types.NewSignatureType(nil, nil, nil, params, results, false) } return p.mallocTy } func (p Program) tyFree() *types.Signature { if p.freeTy == nil { paramPtr := types.NewParam(token.NoPos, nil, "", p.VoidPtr().raw.Type) params := types.NewTuple(paramPtr) p.freeTy = types.NewSignatureType(nil, nil, nil, params, nil, false) } return p.freeTy } func (b Builder) malloc(size Expr) Expr { fn := b.Pkg.cFunc("malloc", b.Prog.tyMalloc()) return b.Call(fn, size) } func (b Builder) free(ptr Expr) Expr { fn := b.Pkg.cFunc("free", b.Prog.tyFree()) return b.Call(fn, ptr) } // ----------------------------------------------------------------------------- // ArrayAlloca reserves space for an array of n elements of type telem. func (b Builder) ArrayAlloca(telem Type, n Expr) (ret Expr) { if debugInstr { log.Printf("ArrayAlloca %v, %v\n", telem.raw.Type, n.impl) } ret.impl = llvm.CreateArrayAlloca(b.impl, telem.ll, n.impl) ret.Type = b.Prog.Pointer(telem) return } /* TODO(xsw): // ArrayAlloc allocates zero initialized space for an array of n elements of type telem. func (b Builder) ArrayAlloc(telem Type, n Expr) (ret Expr) { prog := b.Prog elemSize := SizeOf(prog, telem) size := b.BinOp(token.MUL, n, elemSize) ret.impl = b.AllocZ(size).impl ret.Type = prog.Pointer(telem) return } */ // ----------------------------------------------------------------------------- // AtomicOp is an atomic operation. type AtomicOp = llvm.AtomicRMWBinOp const ( OpXchg = llvm.AtomicRMWBinOpXchg OpAdd = llvm.AtomicRMWBinOpAdd OpSub = llvm.AtomicRMWBinOpSub OpAnd = llvm.AtomicRMWBinOpAnd OpNand = llvm.AtomicRMWBinOpNand OpOr = llvm.AtomicRMWBinOpOr OpXor = llvm.AtomicRMWBinOpXor OpMax = llvm.AtomicRMWBinOpMax OpMin = llvm.AtomicRMWBinOpMin OpUMax = llvm.AtomicRMWBinOpUMax OpUMin = llvm.AtomicRMWBinOpUMin ) // Atomic performs an atomic operation on the memory location pointed to by ptr. func (b Builder) Atomic(op AtomicOp, ptr, val Expr) Expr { if debugInstr { log.Printf("Atomic %v, %v, %v\n", op, ptr.impl, val.impl) } t := b.Prog.Elem(ptr.Type) val = b.ChangeType(t, val) ret := b.impl.CreateAtomicRMW(op, ptr.impl, val.impl, llvm.AtomicOrderingSequentiallyConsistent, false) return Expr{ret, t} } // AtomicCmpXchg performs an atomic compare-and-swap operation on the memory location pointed to by ptr. func (b Builder) AtomicCmpXchg(ptr, old, new Expr) Expr { if debugInstr { log.Printf("AtomicCmpXchg %v, %v, %v\n", ptr.impl, old.impl, new.impl) } prog := b.Prog t := prog.Elem(ptr.Type) old = b.ChangeType(t, old) new = b.ChangeType(t, new) ret := b.impl.CreateAtomicCmpXchg( ptr.impl, old.impl, new.impl, llvm.AtomicOrderingSequentiallyConsistent, llvm.AtomicOrderingSequentiallyConsistent, false) return Expr{ret, prog.Struct(t, prog.Bool())} } // 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} } // -----------------------------------------------------------------------------