From 5d690d8120c70f8a26b9bea65b616c7ae21f9770 Mon Sep 17 00:00:00 2001 From: Rick Guo Date: Sat, 1 Aug 2026 01:33:30 +0800 Subject: [PATCH 01/12] feat: add LLVM escape analysis MVP --- internal/build/build.go | 4 + internal/build/escape_phase1_test.go | 45 ++ .../build/testdata/escape_phase1/escape.go | 11 + internal/escape/escape.go | 641 +++++++++++++++ internal/escape/escape_test.go | 749 ++++++++++++++++++ 5 files changed, 1450 insertions(+) create mode 100644 internal/build/escape_phase1_test.go create mode 100644 internal/build/testdata/escape_phase1/escape.go create mode 100644 internal/escape/escape.go create mode 100644 internal/escape/escape_test.go diff --git a/internal/build/build.go b/internal/build/build.go index 9607c9d00a..1710d05baf 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -48,6 +48,7 @@ import ( "github.com/goplus/llgo/internal/dcepass" "github.com/goplus/llgo/internal/deadcode" "github.com/goplus/llgo/internal/env" + llescape "github.com/goplus/llgo/internal/escape" "github.com/goplus/llgo/internal/firmware" "github.com/goplus/llgo/internal/flash" "github.com/goplus/llgo/internal/goembed" @@ -1833,6 +1834,9 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { } ctx.cTransformer.SetSkipFuncs(cabiSkipFuncsForPlan9Asm(ctx, pkgPath, ret.Module())) + if err := llescape.TransformModule(ret.Module()); err != nil { + return fmt.Errorf("run escape analysis for %v failed: %w", pkgPath, err) + } llabi.LowerLargeAggregates(ctx.prog.TargetData(), ret.Module()) ctx.cTransformer.TransformModule(ret.Path(), ret.Module()) ctx.cTransformer.SetSkipFuncs(nil) diff --git a/internal/build/escape_phase1_test.go b/internal/build/escape_phase1_test.go new file mode 100644 index 0000000000..f5881a29a5 --- /dev/null +++ b/internal/build/escape_phase1_test.go @@ -0,0 +1,45 @@ +//go:build !llgo + +package build + +import ( + "strings" + "testing" + + "github.com/xgo-dev/llvm" +) + +func TestEscapePhase1RunsAfterModuleHook(t *testing.T) { + conf := NewDefaultConf(ModeGen) + var before string + conf.ModuleHook = func(pkg Package) { + if fn := findLLVMFunction(pkg.LPkg.Module(), ".Local"); !fn.IsNil() { + before = fn.String() + } + } + pkgs, err := Do([]string{"./testdata/escape_phase1"}, conf) + if err != nil { + t.Fatalf("generate escape Phase 1 fixture: %v", err) + } + if len(pkgs) != 1 || pkgs[0].LPkg == nil { + t.Fatalf("generated packages = %#v", pkgs) + } + defer pkgs[0].LPkg.Prog.Dispose() + + if !strings.Contains(before, "runtime.AllocZ") { + t.Fatalf("pre-transform Local has no heap allocation:\n%s", before) + } + after := findLLVMFunction(pkgs[0].LPkg.Module(), ".Local").String() + if strings.Contains(after, "runtime.AllocZ") || !strings.Contains(after, "alloca i8") { + t.Fatalf("post-transform Local was not moved to the stack:\n%s", after) + } +} + +func findLLVMFunction(mod llvm.Module, suffix string) llvm.Value { + for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { + if strings.HasSuffix(fn.Name(), suffix) { + return fn + } + } + return llvm.Value{} +} diff --git a/internal/build/testdata/escape_phase1/escape.go b/internal/build/testdata/escape_phase1/escape.go new file mode 100644 index 0000000000..7c4371b57f --- /dev/null +++ b/internal/build/testdata/escape_phase1/escape.go @@ -0,0 +1,11 @@ +package escapephase1 + +func read(p *int) int { + return *p +} + +func Local() int { + p := new(int) + *p = 7 + return read(p) +} diff --git a/internal/escape/escape.go b/internal/escape/escape.go new file mode 100644 index 0000000000..cb658a707d --- /dev/null +++ b/internal/escape/escape.go @@ -0,0 +1,641 @@ +// Package escape implements LLGo's LLVM-IR escape analysis and heap-to-stack +// transform. +package escape + +import ( + "fmt" + "strings" + + llabi "github.com/goplus/llgo/internal/abi" + "github.com/xgo-dev/llvm" +) + +const ( + runtimePrefix = "github.com/goplus/llgo/runtime/internal/runtime." + runtimeAllocZ = runtimePrefix + "AllocZ" + runtimeAllocU = runtimePrefix + "AllocU" + + maxDereferenceLevel = 16 + + // github.com/xgo-dev/llvm does not expose these LLVM 19 C opcode names. + opcodeAtomicCmpXchg = llvm.Opcode(56) + opcodeAtomicRMW = llvm.Opcode(57) + opcodeAddrSpaceCast = llvm.Opcode(60) + opcodeFreeze = llvm.Opcode(68) +) + +type summaryKey struct { + fn llvm.Value + param int +} + +type summaryState uint8 + +const ( + summaryUnseen summaryState = iota + summaryEvaluating + summaryEvaluated +) + +type parameterSummary struct { + heapLevel int + mutatorLevel int + calleeLevel int + results map[int]int + alignments map[int]int +} + +func newParameterSummary() parameterSummary { + return parameterSummary{ + heapLevel: -1, + mutatorLevel: -1, + calleeLevel: -1, + results: make(map[int]int), + alignments: make(map[int]int), + } +} + +func minLevel(dst *int, level int) bool { + if *dst >= 0 && *dst <= level { + return false + } + *dst = level + return true +} + +func (s *parameterSummary) addHeap(level int) bool { + return minLevel(&s.heapLevel, level) +} + +func (s *parameterSummary) addMutator(level int) bool { + return minLevel(&s.mutatorLevel, level) +} + +func (s *parameterSummary) addCallee(level int) bool { + return minLevel(&s.calleeLevel, level) +} + +func (s *parameterSummary) addResult(index, level int) bool { + old, ok := s.results[index] + if ok && old <= level { + return false + } + s.results[index] = level + return true +} + +func (s *parameterSummary) addAlignment(level, align int) bool { + if align <= s.alignments[level] { + return false + } + s.alignments[level] = align + return true +} + +func (s *parameterSummary) join(src parameterSummary) bool { + changed := false + if src.heapLevel >= 0 { + changed = s.addHeap(src.heapLevel) || changed + } + if src.mutatorLevel >= 0 { + changed = s.addMutator(src.mutatorLevel) || changed + } + if src.calleeLevel >= 0 { + changed = s.addCallee(src.calleeLevel) || changed + } + for index, level := range src.results { + changed = s.addResult(index, level) || changed + } + for level, align := range src.alignments { + changed = s.addAlignment(level, align) || changed + } + return changed +} + +type summaryEntry struct { + state summaryState + summary parameterSummary + dependents map[summaryKey]struct{} +} + +type analyzer struct { + mod llvm.Module + + summaries map[summaryKey]*summaryEntry + summaryKeys []summaryKey + pending []summaryKey + pendingSet map[summaryKey]bool +} + +func newAnalyzer(mod llvm.Module) *analyzer { + a := &analyzer{ + mod: mod, + summaries: make(map[summaryKey]*summaryEntry), + pendingSet: make(map[summaryKey]bool), + } + for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { + if fn.IsDeclaration() || isRuntimeFunction(fn) { + continue + } + for index, param := range fn.Params() { + if !isPointer(param.Type()) { + continue + } + key := summaryKey{fn: fn, param: index} + a.summaries[key] = &summaryEntry{ + summary: newParameterSummary(), + dependents: make(map[summaryKey]struct{}), + } + a.summaryKeys = append(a.summaryKeys, key) + } + } + return a +} + +func isRuntimeFunction(fn llvm.Value) bool { + return strings.HasPrefix(fn.Name(), runtimePrefix) +} + +func isPointer(typ llvm.Type) bool { + return typ.TypeKind() == llvm.PointerTypeKind +} + +func (a *analyzer) solveSummaries() { + for _, key := range a.summaryKeys { + a.evaluateSummary(key) + } + for len(a.pending) != 0 { + key := a.pending[0] + a.pending = a.pending[1:] + delete(a.pendingSet, key) + a.evaluateSummary(key) + } +} + +func (a *analyzer) enqueueSummary(key summaryKey) { + if a.pendingSet[key] { + return + } + a.pendingSet[key] = true + a.pending = append(a.pending, key) +} + +func (a *analyzer) evaluateSummary(key summaryKey) { + entry := a.summaries[key] + if entry == nil || entry.state == summaryEvaluating { + return + } + entry.state = summaryEvaluating + result := a.walk(key.fn.Param(key.param), true, key) + entry.state = summaryEvaluated + if entry.summary.join(result.summary) { + for dependent := range entry.dependents { + a.enqueueSummary(dependent) + } + } +} + +func (a *analyzer) summaryFor(key, consumer summaryKey) (parameterSummary, bool) { + entry := a.summaries[key] + if entry == nil { + return parameterSummary{}, false + } + entry.dependents[consumer] = struct{}{} + if entry.state == summaryUnseen { + a.evaluateSummary(key) + } + return entry.summary, true +} + +type useState struct { + user llvm.Value + operand int + derefs int +} + +type walkResult struct { + escaped bool + alignment int + summary parameterSummary +} + +func newWalkResult(parameter bool) walkResult { + result := walkResult{} + if parameter { + result.summary = newParameterSummary() + } + return result +} + +func addUses(worklist *[]useState, value llvm.Value, derefs int) { + for use := value.FirstUse(); !use.IsNil(); use = use.NextUse() { + user := use.User() + for operand := 0; operand < user.OperandsCount(); operand++ { + if user.Operand(operand) == value { + *worklist = append(*worklist, useState{user: user, operand: operand, derefs: derefs}) + } + } + } +} + +func (a *analyzer) walk(root llvm.Value, parameter bool, consumer summaryKey) walkResult { + result := newWalkResult(parameter) + worklist := make([]useState, 0, 8) + addUses(&worklist, root, 0) + visited := make(map[useState]struct{}) + + for len(worklist) != 0 { + state := worklist[len(worklist)-1] + worklist = worklist[:len(worklist)-1] + if _, ok := visited[state]; ok { + continue + } + visited[state] = struct{}{} + if state.derefs > maxDereferenceLevel { + a.recordEscape(&result, parameter, maxDereferenceLevel) + continue + } + + user := state.user + opcode := user.InstructionOpcode() + switch opcode { + case llvm.Load: + a.recordAddressUse(&result, parameter, state.derefs, user.Alignment(), false) + if parameter && isPointer(user.Type()) { + addUses(&worklist, user, state.derefs+1) + } + case llvm.Store: + switch state.operand { + case 0: + copies, ok := exactLocalCopies(user) + if !ok { + a.recordEscape(&result, parameter, state.derefs) + continue + } + for _, copy := range copies { + addUses(&worklist, copy, state.derefs) + } + case 1: + a.recordAddressUse(&result, parameter, state.derefs, user.Alignment(), true) + default: + a.recordEscape(&result, parameter, state.derefs) + } + case llvm.GetElementPtr: + if state.operand == 0 { + addUses(&worklist, user, state.derefs) + } else { + a.recordEscape(&result, parameter, state.derefs) + } + case llvm.BitCast, llvm.PHI: + addUses(&worklist, user, state.derefs) + case llvm.Select: + if state.operand == 1 || state.operand == 2 { + addUses(&worklist, user, state.derefs) + } else { + a.recordEscape(&result, parameter, state.derefs) + } + case opcodeFreeze: + if state.operand == 0 { + addUses(&worklist, user, state.derefs) + } else { + a.recordEscape(&result, parameter, state.derefs) + } + case llvm.ICmp: + // A comparison observes pointer identity but does not retain it. + case llvm.Ret: + if parameter && state.operand == 0 { + result.summary.addResult(0, state.derefs) + } else { + a.recordEscape(&result, parameter, state.derefs) + } + case llvm.Call, llvm.Invoke: + a.handleCall(&result, &worklist, state, parameter, consumer) + case opcodeAtomicCmpXchg: + switch state.operand { + case 0: + a.recordAddressUse(&result, parameter, state.derefs, user.Alignment(), true) + case 1: + // Comparing the expected value does not publish it. + default: + a.recordEscape(&result, parameter, state.derefs) + } + case opcodeAtomicRMW: + if state.operand == 0 { + a.recordAddressUse(&result, parameter, state.derefs, user.Alignment(), true) + } else { + a.recordEscape(&result, parameter, state.derefs) + } + case llvm.PtrToInt, llvm.IntToPtr, opcodeAddrSpaceCast: + a.recordEscape(&result, parameter, state.derefs) + default: + a.recordEscape(&result, parameter, state.derefs) + } + } + return result +} + +func (a *analyzer) recordEscape(result *walkResult, parameter bool, level int) { + if parameter { + result.summary.addHeap(level) + return + } + result.escaped = true +} + +func (a *analyzer) recordAddressUse(result *walkResult, parameter bool, level, align int, mutator bool) { + if parameter { + result.summary.addAlignment(level, align) + if mutator { + result.summary.addMutator(level) + } + return + } + if align > result.alignment { + result.alignment = align + } +} + +func exactLocalCopies(store llvm.Value) ([]llvm.Value, bool) { + if store.IsVolatile() || store.Ordering() != llvm.AtomicOrderingNotAtomic { + return nil, false + } + slot := store.Operand(1).IsAAllocaInst() + if slot.IsNil() { + return nil, false + } + + stores := 0 + var copies []llvm.Value + for use := slot.FirstUse(); !use.IsNil(); use = use.NextUse() { + user := use.User() + switch { + case !user.IsAStoreInst().IsNil() && user.Operand(1) == slot: + stores++ + if user != store || user.IsVolatile() || user.Ordering() != llvm.AtomicOrderingNotAtomic { + return nil, false + } + case !user.IsALoadInst().IsNil() && user.Operand(0) == slot: + if user.IsVolatile() || user.Ordering() != llvm.AtomicOrderingNotAtomic || !isPointer(user.Type()) { + return nil, false + } + copies = append(copies, user) + default: + return nil, false + } + } + return copies, stores == 1 +} + +func callCalleeOperand(call llvm.Value) int { + called := call.CalledValue() + index := -1 + for operand := 0; operand < call.OperandsCount(); operand++ { + if call.Operand(operand) == called { + index = operand + } + } + return index +} + +func (a *analyzer) handleCall(result *walkResult, worklist *[]useState, state useState, parameter bool, consumer summaryKey) { + call := state.user + calleeOperand := callCalleeOperand(call) + if state.operand == calleeOperand { + if parameter { + result.summary.addCallee(state.derefs) + } else { + result.escaped = true + } + return + } + + callee := call.CalledValue() + if !callee.IsAInlineAsm().IsNil() { + a.recordEscape(result, parameter, state.derefs) + return + } + fn := callee.IsAFunction() + if fn.IsNil() { + a.recordEscape(result, parameter, state.derefs) + return + } + if fn.IntrinsicID() != 0 { + if !a.handleIntrinsic(result, state, parameter) { + a.recordEscape(result, parameter, state.derefs) + } + return + } + if fn.IsDeclaration() || isRuntimeFunction(fn) || state.operand >= fn.ParamsCount() { + a.recordEscape(result, parameter, state.derefs) + return + } + + calleeSummary, ok := a.summaryFor(summaryKey{fn: fn, param: state.operand}, consumer) + if !ok { + a.recordEscape(result, parameter, state.derefs) + return + } + a.composeSummary(result, worklist, call, state.derefs, parameter, calleeSummary) +} + +func (a *analyzer) handleIntrinsic(result *walkResult, state useState, parameter bool) bool { + name := state.user.CalledValue().Name() + switch { + case strings.HasPrefix(name, "llvm.dbg."), + strings.HasPrefix(name, "llvm.lifetime.start."), + strings.HasPrefix(name, "llvm.lifetime.end."), + strings.HasPrefix(name, "llvm.objectsize."): + return true + case strings.HasPrefix(name, "llvm.memset."): + if state.operand == 0 { + a.recordAddressUse(result, parameter, state.derefs, 0, true) + return true + } + case strings.HasPrefix(name, "llvm.memcpy."), strings.HasPrefix(name, "llvm.memmove."): + if state.operand == 0 { + a.recordAddressUse(result, parameter, state.derefs, 0, true) + return true + } + if state.operand == 1 { + return true + } + } + return false +} + +func (a *analyzer) composeSummary(result *walkResult, worklist *[]useState, call llvm.Value, level int, parameter bool, summary parameterSummary) { + if summary.heapLevel >= 0 { + if parameter { + result.summary.addHeap(level + summary.heapLevel) + } else if summary.heapLevel == 0 { + result.escaped = true + } + } + if parameter { + if summary.mutatorLevel >= 0 { + result.summary.addMutator(level + summary.mutatorLevel) + } + if summary.calleeLevel >= 0 { + result.summary.addCallee(level + summary.calleeLevel) + } + for derefs, align := range summary.alignments { + result.summary.addAlignment(level+derefs, align) + } + } else if align := summary.alignments[0]; align > result.alignment { + result.alignment = align + } + + for index, derefs := range summary.results { + if index != 0 || !isPointer(call.Type()) { + a.recordEscape(result, parameter, level) + continue + } + if !parameter && derefs != 0 { + continue + } + addUses(worklist, call, level+derefs) + } +} + +type allocationPlan struct { + call llvm.Value + size llvm.Value + zero bool + alignment int +} + +// TransformModule analyzes eligible LLGo allocations, rewrites proven-local +// AllocZ and AllocU calls, and verifies the resulting LLVM module. +func TransformModule(mod llvm.Module) error { + a := newAnalyzer(mod) + a.solveSummaries() + + var plans []allocationPlan + for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { + if fn.IsDeclaration() { + continue + } + for block := fn.FirstBasicBlock(); !block.IsNil(); block = llvm.NextBasicBlock(block) { + for instr := block.FirstInstruction(); !instr.IsNil(); instr = llvm.NextInstruction(instr) { + plan, ok := a.planAllocation(instr) + if ok { + plans = append(plans, plan) + } + } + } + } + + for _, plan := range plans { + rewriteAllocation(mod.Context(), plan) + } + if len(plans) != 0 { + if err := llvm.VerifyModule(mod, llvm.ReturnStatusAction); err != nil { + return fmt.Errorf("verify heap-to-stack transform: %w", err) + } + } + return nil +} + +func (a *analyzer) planAllocation(instr llvm.Value) (allocationPlan, bool) { + call := instr.IsACallInst() + if call.IsNil() || call.OperandsCount() < 2 || !isPointer(call.Type()) { + return allocationPlan{}, false + } + callee := call.CalledValue().IsAFunction() + if callee.IsNil() { + return allocationPlan{}, false + } + name := callee.Name() + if name != runtimeAllocZ && name != runtimeAllocU { + return allocationPlan{}, false + } + size := call.Operand(0).IsAConstantInt() + if size.IsNil() { + return allocationPlan{}, false + } + sizeBytes := size.ZExtValue() + if sizeBytes == 0 || sizeBytes > llabi.MaxImplicitStackVarSize || blockInCycle(call.InstructionParent()) { + return allocationPlan{}, false + } + + result := a.walk(call, false, summaryKey{}) + if result.escaped { + return allocationPlan{}, false + } + alignment := callResultAlignment(call, callee) + if result.alignment > alignment { + alignment = result.alignment + } + if alignment == 0 { + alignment = 1 + } + if alignment&(alignment-1) != 0 || alignment > int(llabi.MaxImplicitStackVarSize) { + return allocationPlan{}, false + } + return allocationPlan{call: call, size: size, zero: name == runtimeAllocZ, alignment: alignment}, true +} + +func callResultAlignment(call, callee llvm.Value) int { + kind := llvm.AttributeKindID("align") + align := 0 + if attr := call.GetCallSiteEnumAttribute(0, kind); !attr.IsNil() { + align = int(attr.GetEnumValue()) + } + if attr := callee.GetEnumAttributeAtIndex(0, kind); !attr.IsNil() && int(attr.GetEnumValue()) > align { + align = int(attr.GetEnumValue()) + } + return align +} + +func blockInCycle(start llvm.BasicBlock) bool { + visited := make(map[llvm.BasicBlock]bool) + var visit func(llvm.BasicBlock) bool + visit = func(block llvm.BasicBlock) bool { + if block == start { + return true + } + if visited[block] { + return false + } + visited[block] = true + terminator := block.LastInstruction() + for index := 0; index < terminator.SuccessorsCount(); index++ { + if visit(terminator.Successor(index)) { + return true + } + } + return false + } + + terminator := start.LastInstruction() + for index := 0; index < terminator.SuccessorsCount(); index++ { + if visit(terminator.Successor(index)) { + return true + } + } + return false +} + +func rewriteAllocation(ctx llvm.Context, plan allocationPlan) bool { + builder := ctx.NewBuilder() + defer builder.Dispose() + builder.SetInsertPointBefore(plan.call) + stack := llvm.CreateArrayAlloca(builder, ctx.Int8Type(), plan.size) + if stack.Type().PointerAddressSpace() != plan.call.Type().PointerAddressSpace() { + stack.EraseFromParentAsInstruction() + return false + } + stack.SetName(plan.call.Name() + ".stack") + stack.SetAlignment(plan.alignment) + if plan.zero { + builder.CreateIntrinsic(ctx.VoidType(), llvm.LookupIntrinsicID("llvm.memset"), []llvm.Value{ + stack, + llvm.ConstInt(ctx.Int8Type(), 0, false), + plan.size, + llvm.ConstInt(ctx.Int1Type(), 0, false), + }, "") + } + plan.call.ReplaceAllUsesWith(stack) + plan.call.EraseFromParentAsInstruction() + return true +} diff --git a/internal/escape/escape_test.go b/internal/escape/escape_test.go new file mode 100644 index 0000000000..c3fe483bef --- /dev/null +++ b/internal/escape/escape_test.go @@ -0,0 +1,749 @@ +//go:build !llgo + +package escape + +import ( + "os" + "path/filepath" + "runtime" + "strings" + "testing" + + llabi "github.com/goplus/llgo/internal/abi" + "github.com/goplus/llgo/internal/cabi" + "github.com/goplus/llgo/ssa" + "github.com/xgo-dev/llvm" +) + +const allocationDeclarations = ` +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocZ"(i64) +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) +` + +func parseModule(t *testing.T, ir string) (llvm.Context, llvm.Module) { + t.Helper() + ctx := llvm.NewContext() + path := filepath.Join(t.TempDir(), "input.ll") + if err := os.WriteFile(path, []byte(ir), 0o644); err != nil { + ctx.Dispose() + t.Fatal(err) + } + buffer, err := llvm.NewMemoryBufferFromFile(path) + if err != nil { + ctx.Dispose() + t.Fatal(err) + } + mod, err := ctx.ParseIR(buffer) + if err != nil { + ctx.Dispose() + t.Fatalf("parse LLVM IR: %v\n%s", err, ir) + } + return ctx, mod +} + +func transformModule(t *testing.T, ir string) (llvm.Context, llvm.Module) { + t.Helper() + ctx, mod := parseModule(t, ir) + if err := TransformModule(mod); err != nil { + mod.Dispose() + ctx.Dispose() + t.Fatalf("TransformModule: %v\n%s", err, mod.String()) + } + return ctx, mod +} + +func requireStackAllocation(t *testing.T, mod llvm.Module, name string) { + t.Helper() + ir := mod.NamedFunction(name).String() + if strings.Contains(ir, runtimeAllocZ) || strings.Contains(ir, runtimeAllocU) { + t.Fatalf("%s allocation was not moved to the stack:\n%s", name, ir) + } + if !strings.Contains(ir, "alloca i8") { + t.Fatalf("%s has no stack allocation:\n%s", name, ir) + } +} + +func requireHeapAllocation(t *testing.T, mod llvm.Module, name string) { + t.Helper() + ir := mod.NamedFunction(name).String() + if !strings.Contains(ir, runtimeAllocZ) && !strings.Contains(ir, runtimeAllocU) { + t.Fatalf("%s allocation unexpectedly moved to the stack:\n%s", name, ir) + } +} + +func TestSafeUsesAndTransparentPropagation(t *testing.T) { + ir := ` +target datalayout = "e-p:64:64-i64:64-n32:64-S128" +` + allocationDeclarations + ` +declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) +declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) +declare void @llvm.memset.p0.i64(ptr writeonly, i8, i64, i1 immarg) +declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly, ptr noalias readonly, i64, i1 immarg) +@bytes = global [8 x i8] zeroinitializer + +define i64 @load_store() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i64 7, ptr %p, align 8 + %v = load i64, ptr %p, align 8 + ret i64 %v +} + +define void @gep() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = getelementptr i8, ptr %p, i64 1 + store i8 7, ptr %q, align 1 + ret void +} + +define void @bitcast() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = bitcast ptr %p to ptr + store i8 7, ptr %q, align 1 + ret void +} + +define void @select_value(i1 %cond) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = select i1 %cond, ptr %p, ptr %p + store i8 7, ptr %q, align 1 + ret void +} + +define void @phi_cycle(i1 %again) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + br label %loop +loop: + %q = phi ptr [ %p, %entry ], [ %q, %loop ] + store i8 7, ptr %q, align 1 + br i1 %again, label %loop, label %exit +exit: + ret void +} + +define i1 @compare() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %same = icmp eq ptr %p, null + ret i1 %same +} + +define void @atomic_addresses() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store atomic i32 1, ptr %p monotonic, align 4 + %v = load atomic i32, ptr %p monotonic, align 4 + %cx = cmpxchg ptr %p, i32 %v, i32 2 monotonic monotonic, align 4 + %rmw = atomicrmw add ptr %p, i32 1 monotonic, align 4 + ret void +} + +define void @freeze_value() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = freeze ptr %p + store i8 7, ptr %q + ret void +} + +define void @volatile_addresses() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store volatile i8 7, ptr %p + %v = load volatile i8, ptr %p + ret void +} + +define void @safe_intrinsics() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @llvm.lifetime.start.p0(i64 8, ptr %p) + call void @llvm.memset.p0.i64(ptr %p, i8 0, i64 8, i1 false) + call void @llvm.memcpy.p0.p0.i64(ptr @bytes, ptr %p, i64 8, i1 false) + call void @llvm.lifetime.end.p0(i64 8, ptr %p) + ret void +} + +define void @cmpxchg_expected() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %old = cmpxchg ptr @escaped_pointer, ptr %p, ptr null monotonic monotonic, align 8 + ret void +} + +@escaped_pointer = global ptr null +` + ctx, mod := transformModule(t, ir) + defer ctx.Dispose() + defer mod.Dispose() + for _, name := range []string{"load_store", "gep", "bitcast", "select_value", "phi_cycle", "compare", "atomic_addresses", "freeze_value", "volatile_addresses", "safe_intrinsics", "cmpxchg_expected"} { + requireStackAllocation(t, mod, name) + } + if got := mod.NamedFunction("load_store").String(); !strings.Contains(got, "alloca i8, i64 8, align 8") { + t.Fatalf("load alignment was not preserved:\n%s", got) + } +} + +func TestExactLocalCopy(t *testing.T) { + ir := ` +target datalayout = "e-p:64:64-i64:64-n32:64-S128" +@escaped = global ptr null +` + allocationDeclarations + ` +declare void @clobber(ptr) + +define void @exact_copy() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr %p, ptr %slot + %q = load ptr, ptr %slot + store i8 1, ptr %q + ret void +} + +define void @alias() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + %alias = getelementptr ptr, ptr %slot, i64 0 + store ptr %p, ptr %slot + %q = load ptr, ptr %alias + store i8 1, ptr %q + ret void +} + +define void @slot_address_escape() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr %p, ptr %slot + store ptr %slot, ptr @escaped + ret void +} + +define void @unknown_clobber() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr %p, ptr %slot + call void @clobber(ptr %slot) + ret void +} + +define void @unsupported_reader() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr %p, ptr %slot + %q = load i64, ptr %slot + ret void +} + +define void @other_store() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr %p, ptr %slot + store ptr null, ptr %slot + ret void +} + +define void @global_destination() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store ptr %p, ptr @escaped + ret void +} + +define void @nonlocal_destination(ptr %dst) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store ptr %p, ptr %dst + ret void +} +` + ctx, mod := transformModule(t, ir) + defer ctx.Dispose() + defer mod.Dispose() + requireStackAllocation(t, mod, "exact_copy") + for _, name := range []string{"alias", "slot_address_escape", "unknown_clobber", "unsupported_reader", "other_store", "global_destination", "nonlocal_destination"} { + requireHeapAllocation(t, mod, name) + } +} + +func TestDirectCallSummariesAndRecursion(t *testing.T) { + ir := ` +target datalayout = "e-p:64:64-i64:64-n32:64-S128" +@escaped = global ptr null +` + allocationDeclarations + ` + +define void @read(ptr %p) { +entry: + %v = load i8, ptr %p + ret void +} + +define void @save(ptr %p) { +entry: + store ptr %p, ptr @escaped + ret void +} + +define ptr @identity(ptr %p) { +entry: + ret ptr %p +} + +define void @self_safe(ptr %p) { +entry: + call void @self_safe(ptr %p) + ret void +} + +define void @self_sink(ptr %p) { +entry: + call void @self_sink(ptr %p) + store ptr %p, ptr @escaped + ret void +} + +define void @mutual_safe_a(ptr %p) { +entry: + call void @mutual_safe_b(ptr %p) + ret void +} + +define void @mutual_safe_b(ptr %p) { +entry: + call void @mutual_safe_a(ptr %p) + ret void +} + +define void @mutual_sink_a(ptr %p) { +entry: + call void @mutual_sink_b(ptr %p) + ret void +} + +define void @mutual_sink_b(ptr %p) { +entry: + call void @mutual_sink_a(ptr %p) + store ptr %p, ptr @escaped + ret void +} + +define void @known_no_leak() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @read(ptr %p) + ret void +} + +define void @known_heap_leak() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @save(ptr %p) + ret void +} + +define void @result_local() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = call ptr @identity(ptr %p) + store i8 1, ptr %q + ret void +} + +define void @result_escape() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = call ptr @identity(ptr %p) + store ptr %q, ptr @escaped + ret void +} + +define void @direct_recursion_safe() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @self_safe(ptr %p) + ret void +} + +define void @direct_recursion_sink() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @self_sink(ptr %p) + ret void +} + +define void @mutual_recursion_safe() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @mutual_safe_a(ptr %p) + ret void +} + +define void @mutual_recursion_sink() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @mutual_sink_a(ptr %p) + ret void +} +` + ctx, mod := transformModule(t, ir) + defer ctx.Dispose() + defer mod.Dispose() + for _, name := range []string{"known_no_leak", "result_local", "direct_recursion_safe", "mutual_recursion_safe"} { + requireStackAllocation(t, mod, name) + } + for _, name := range []string{"known_heap_leak", "result_escape", "direct_recursion_sink", "mutual_recursion_sink"} { + requireHeapAllocation(t, mod, name) + } +} + +func TestUnknownCallBoundaries(t *testing.T) { + ir := ` +target datalayout = "e-p:64:64-i64:64-n32:64-S128" +` + allocationDeclarations + ` +declare void @c_function(ptr) +declare void @"github.com/goplus/llgo/runtime/internal/runtime.ChanSend"(ptr) +declare ptr @llvm.launder.invariant.group.p0(ptr) + +define void @bodyless_c() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @c_function(ptr %p) + ret void +} + +define void @runtime_call() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @"github.com/goplus/llgo/runtime/internal/runtime.ChanSend"(ptr %p) + ret void +} + +define void @indirect_call(ptr %fn) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void %fn(ptr %p) + ret void +} + +define void @inline_asm() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void asm sideeffect "", "r"(ptr %p) + ret void +} + +define void @unknown_intrinsic() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = call ptr @llvm.launder.invariant.group.p0(ptr %p) + ret void +} +` + ctx, mod := transformModule(t, ir) + defer ctx.Dispose() + defer mod.Dispose() + for _, name := range []string{"bodyless_c", "runtime_call", "indirect_call", "inline_asm", "unknown_intrinsic"} { + requireHeapAllocation(t, mod, name) + } +} + +func TestInvokeUsesDirectSummaryAndRejectsUnknownCallee(t *testing.T) { + ir := ` +target datalayout = "e-p:64:64-i64:64-n32:64-S128" +` + allocationDeclarations + ` +declare i32 @personality(...) +declare void @external(ptr) + +define void @read_invoke(ptr %p) { +entry: + %v = load i8, ptr %p + ret void +} + +define void @known_invoke() personality ptr @personality { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + invoke void @read_invoke(ptr %p) to label %done unwind label %failed +done: + ret void +failed: + %landing = landingpad { ptr, i32 } cleanup + resume { ptr, i32 } %landing +} + +define void @unknown_invoke() personality ptr @personality { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + invoke void @external(ptr %p) to label %done unwind label %failed +done: + ret void +failed: + %landing = landingpad { ptr, i32 } cleanup + resume { ptr, i32 } %landing +} +` + ctx, mod := transformModule(t, ir) + defer ctx.Dispose() + defer mod.Dispose() + requireStackAllocation(t, mod, "known_invoke") + requireHeapAllocation(t, mod, "unknown_invoke") +} + +func TestConversionsReturnsAndAtomicPublicationEscape(t *testing.T) { + ir := ` +target datalayout = "e-p:64:64-p1:64:64-i64:64-n32:64-S128" +@escaped = global ptr null +` + allocationDeclarations + ` + +define i64 @ptr_to_int() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %n = ptrtoint ptr %p to i64 + ret i64 %n +} + +define void @address_space_cast() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = addrspacecast ptr %p to ptr addrspace(1) + ret void +} + +define ptr @returned() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + ret ptr %p +} + +define void @atomic_store_value() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store atomic ptr %p, ptr @escaped release, align 8 + ret void +} + +define void @cmpxchg_replacement() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %old = cmpxchg ptr @escaped, ptr null, ptr %p monotonic monotonic, align 8 + ret void +} + +define void @atomicrmw_value() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %old = atomicrmw xchg ptr @escaped, ptr %p monotonic, align 8 + ret void +} +` + ctx, mod := transformModule(t, ir) + defer ctx.Dispose() + defer mod.Dispose() + for _, name := range []string{"ptr_to_int", "address_space_cast", "returned", "atomic_store_value", "cmpxchg_replacement", "atomicrmw_value"} { + requireHeapAllocation(t, mod, name) + } +} + +func TestParameterSummaryFacts(t *testing.T) { + ir := ` +target datalayout = "e-p:64:64-i64:64-n32:64-S128" +@escaped = global ptr null + +define ptr @facts(ptr %p) { +entry: + %q = load ptr, ptr %p, align 8 + store ptr %q, ptr @escaped + store i8 1, ptr %p, align 4 + ret ptr %p +} + +define ptr @forward(ptr %p) { +entry: + %q = call ptr @facts(ptr %p) + ret ptr %q +} + +define void @call_parameter(ptr %fn) { +entry: + call void %fn() + ret void +} +` + ctx, mod := parseModule(t, ir) + defer ctx.Dispose() + defer mod.Dispose() + a := newAnalyzer(mod) + a.solveSummaries() + + facts := a.summaries[summaryKey{fn: mod.NamedFunction("facts"), param: 0}].summary + if facts.heapLevel != 1 || facts.mutatorLevel != 0 || facts.results[0] != 0 || facts.alignments[0] != 8 { + t.Fatalf("facts summary = %#v", facts) + } + forward := a.summaries[summaryKey{fn: mod.NamedFunction("forward"), param: 0}].summary + if forward.heapLevel != 1 || forward.mutatorLevel != 0 || forward.results[0] != 0 || forward.alignments[0] != 8 { + t.Fatalf("forward summary = %#v", forward) + } + callee := a.summaries[summaryKey{fn: mod.NamedFunction("call_parameter"), param: 0}].summary + if callee.calleeLevel != 0 || callee.heapLevel != -1 { + t.Fatalf("callee summary = %#v", callee) + } +} + +func TestAllocationRewriteAndPolicyRejections(t *testing.T) { + ir := ` +target datalayout = "e-p:64:64-i64:64-n32:64-S128" +@escaped = global ptr null +` + allocationDeclarations + ` + +define void @alloc_z() { +entry: + %p = call align 16 ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocZ"(i64 8) + store i8 1, ptr %p + ret void +} + +define void @alloc_u() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i8 1, ptr %p + ret void +} + +define void @escaping() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store ptr %p, ptr @escaped + ret void +} + +define void @loop(i1 %again) { +entry: + br label %body +body: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i8 1, ptr %p + br i1 %again, label %body, label %exit +exit: + ret void +} + +define void @dynamic_size(i64 %size) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 %size) + store i8 1, ptr %p + ret void +} + +define void @too_large() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 65537) + store i8 1, ptr %p + ret void +} + +define void @at_limit() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 65536) + store i8 1, ptr %p + ret void +} + +define void @zero_size() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 0) + %same = icmp eq ptr %p, null + ret void +} + +define void @excessive_alignment() { +entry: + %p = call align 131072 ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i8 1, ptr %p + ret void +} +` + ctx, mod := transformModule(t, ir) + defer ctx.Dispose() + defer mod.Dispose() + for _, name := range []string{"alloc_z", "alloc_u", "at_limit"} { + requireStackAllocation(t, mod, name) + } + for _, name := range []string{"escaping", "loop", "dynamic_size", "too_large", "zero_size", "excessive_alignment"} { + requireHeapAllocation(t, mod, name) + } + zeroed := mod.NamedFunction("alloc_z").String() + if !strings.Contains(zeroed, "alloca i8, i64 8, align 16") || !strings.Contains(zeroed, "call void @llvm.memset") { + t.Fatalf("AllocZ initialization/alignment was not preserved:\n%s", zeroed) + } + uninitialized := mod.NamedFunction("alloc_u").String() + if strings.Contains(uninitialized, "llvm.memset") { + t.Fatalf("AllocU was unexpectedly initialized:\n%s", uninitialized) + } +} + +func TestAllocaAddressSpaceMismatchRejectsRewrite(t *testing.T) { + ir := ` +target datalayout = "e-p:64:64-A5" +` + allocationDeclarations + ` + +define void @wrong_address_space() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i8 1, ptr %p + ret void +} +` + ctx, mod := transformModule(t, ir) + defer ctx.Dispose() + defer mod.Dispose() + requireHeapAllocation(t, mod, "wrong_address_space") +} + +func TestTransformPrecedesLargeAggregateAndCABILowering(t *testing.T) { + llvm.InitializeAllTargetInfos() + llvm.InitializeAllTargets() + llvm.InitializeAllTargetMCs() + ir := ` +%Large = type [65537 x i8] +` + allocationDeclarations + ` + +define void @"pkg.local"() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i8 1, ptr %p + ret void +} + +define %Large @"pkg.large"(ptr %src) { +entry: + %value = load %Large, ptr %src, align 1 + ret %Large %value +} +` + ctx, mod := parseModule(t, ir) + defer ctx.Dispose() + defer mod.Dispose() + prog := ssa.NewProgram(&ssa.Target{GOOS: runtime.GOOS, GOARCH: runtime.GOARCH}) + defer prog.Dispose() + mod.SetDataLayout(prog.DataLayout()) + mod.SetTarget(prog.Target().Spec().Triple) + + if err := TransformModule(mod); err != nil { + t.Fatalf("escape transform: %v", err) + } + requireStackAllocation(t, mod, "pkg.local") + llabi.LowerLargeAggregates(prog.TargetData(), mod) + cabi.NewTransformer(prog, "", "", cabi.ModeAllFunc, false).TransformModule("pkg", mod) + if err := llvm.VerifyModule(mod, llvm.ReturnStatusAction); err != nil { + t.Fatalf("module invalid after escape, large aggregate, and C ABI transforms: %v\n%s", err, mod.String()) + } +} From fadef10726a9c1c0b4a9258100a2c1e8b05d5851 Mon Sep 17 00:00:00 2001 From: Rick Guo Date: Sat, 1 Aug 2026 01:43:44 +0800 Subject: [PATCH 02/12] test: move escape IR cases to fixtures --- internal/escape/escape_test.go | 767 +----------------- internal/escape/testdata/address-space/in.txt | 10 + .../escape/testdata/address-space/out.txt | 12 + internal/escape/testdata/calls/in.txt | 158 ++++ internal/escape/testdata/calls/out.txt | 160 ++++ internal/escape/testdata/escaping-uses/in.txt | 46 ++ .../escape/testdata/escaping-uses/out.txt | 48 ++ internal/escape/testdata/exact-copy/in.txt | 77 ++ internal/escape/testdata/exact-copy/out.txt | 80 ++ internal/escape/testdata/invoke/in.txt | 33 + internal/escape/testdata/invoke/out.txt | 45 + .../escape/testdata/rewrite-policy/in.txt | 73 ++ .../escape/testdata/rewrite-policy/out.txt | 84 ++ internal/escape/testdata/safe-uses/in.txt | 104 +++ internal/escape/testdata/safe-uses/out.txt | 120 +++ internal/escape/testdata/unknown-calls/in.txt | 41 + .../escape/testdata/unknown-calls/out.txt | 49 ++ 17 files changed, 1175 insertions(+), 732 deletions(-) create mode 100644 internal/escape/testdata/address-space/in.txt create mode 100644 internal/escape/testdata/address-space/out.txt create mode 100644 internal/escape/testdata/calls/in.txt create mode 100644 internal/escape/testdata/calls/out.txt create mode 100644 internal/escape/testdata/escaping-uses/in.txt create mode 100644 internal/escape/testdata/escaping-uses/out.txt create mode 100644 internal/escape/testdata/exact-copy/in.txt create mode 100644 internal/escape/testdata/exact-copy/out.txt create mode 100644 internal/escape/testdata/invoke/in.txt create mode 100644 internal/escape/testdata/invoke/out.txt create mode 100644 internal/escape/testdata/rewrite-policy/in.txt create mode 100644 internal/escape/testdata/rewrite-policy/out.txt create mode 100644 internal/escape/testdata/safe-uses/in.txt create mode 100644 internal/escape/testdata/safe-uses/out.txt create mode 100644 internal/escape/testdata/unknown-calls/in.txt create mode 100644 internal/escape/testdata/unknown-calls/out.txt diff --git a/internal/escape/escape_test.go b/internal/escape/escape_test.go index c3fe483bef..d152ce0769 100644 --- a/internal/escape/escape_test.go +++ b/internal/escape/escape_test.go @@ -5,745 +5,48 @@ package escape import ( "os" "path/filepath" - "runtime" - "strings" "testing" - llabi "github.com/goplus/llgo/internal/abi" - "github.com/goplus/llgo/internal/cabi" - "github.com/goplus/llgo/ssa" + "github.com/goplus/llgo/internal/littest" "github.com/xgo-dev/llvm" ) -const allocationDeclarations = ` -declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocZ"(i64) -declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) -` - -func parseModule(t *testing.T, ir string) (llvm.Context, llvm.Module) { - t.Helper() - ctx := llvm.NewContext() - path := filepath.Join(t.TempDir(), "input.ll") - if err := os.WriteFile(path, []byte(ir), 0o644); err != nil { - ctx.Dispose() - t.Fatal(err) - } - buffer, err := llvm.NewMemoryBufferFromFile(path) +func TestTransformModule(t *testing.T) { + entries, err := os.ReadDir("testdata") if err != nil { - ctx.Dispose() t.Fatal(err) } - mod, err := ctx.ParseIR(buffer) - if err != nil { - ctx.Dispose() - t.Fatalf("parse LLVM IR: %v\n%s", err, ir) - } - return ctx, mod -} - -func transformModule(t *testing.T, ir string) (llvm.Context, llvm.Module) { - t.Helper() - ctx, mod := parseModule(t, ir) - if err := TransformModule(mod); err != nil { - mod.Dispose() - ctx.Dispose() - t.Fatalf("TransformModule: %v\n%s", err, mod.String()) - } - return ctx, mod -} - -func requireStackAllocation(t *testing.T, mod llvm.Module, name string) { - t.Helper() - ir := mod.NamedFunction(name).String() - if strings.Contains(ir, runtimeAllocZ) || strings.Contains(ir, runtimeAllocU) { - t.Fatalf("%s allocation was not moved to the stack:\n%s", name, ir) - } - if !strings.Contains(ir, "alloca i8") { - t.Fatalf("%s has no stack allocation:\n%s", name, ir) - } -} - -func requireHeapAllocation(t *testing.T, mod llvm.Module, name string) { - t.Helper() - ir := mod.NamedFunction(name).String() - if !strings.Contains(ir, runtimeAllocZ) && !strings.Contains(ir, runtimeAllocU) { - t.Fatalf("%s allocation unexpectedly moved to the stack:\n%s", name, ir) - } -} - -func TestSafeUsesAndTransparentPropagation(t *testing.T) { - ir := ` -target datalayout = "e-p:64:64-i64:64-n32:64-S128" -` + allocationDeclarations + ` -declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) -declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) -declare void @llvm.memset.p0.i64(ptr writeonly, i8, i64, i1 immarg) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly, ptr noalias readonly, i64, i1 immarg) -@bytes = global [8 x i8] zeroinitializer - -define i64 @load_store() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - store i64 7, ptr %p, align 8 - %v = load i64, ptr %p, align 8 - ret i64 %v -} - -define void @gep() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %q = getelementptr i8, ptr %p, i64 1 - store i8 7, ptr %q, align 1 - ret void -} - -define void @bitcast() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %q = bitcast ptr %p to ptr - store i8 7, ptr %q, align 1 - ret void -} - -define void @select_value(i1 %cond) { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %q = select i1 %cond, ptr %p, ptr %p - store i8 7, ptr %q, align 1 - ret void -} - -define void @phi_cycle(i1 %again) { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - br label %loop -loop: - %q = phi ptr [ %p, %entry ], [ %q, %loop ] - store i8 7, ptr %q, align 1 - br i1 %again, label %loop, label %exit -exit: - ret void -} - -define i1 @compare() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %same = icmp eq ptr %p, null - ret i1 %same -} - -define void @atomic_addresses() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - store atomic i32 1, ptr %p monotonic, align 4 - %v = load atomic i32, ptr %p monotonic, align 4 - %cx = cmpxchg ptr %p, i32 %v, i32 2 monotonic monotonic, align 4 - %rmw = atomicrmw add ptr %p, i32 1 monotonic, align 4 - ret void -} - -define void @freeze_value() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %q = freeze ptr %p - store i8 7, ptr %q - ret void -} - -define void @volatile_addresses() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - store volatile i8 7, ptr %p - %v = load volatile i8, ptr %p - ret void -} - -define void @safe_intrinsics() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - call void @llvm.lifetime.start.p0(i64 8, ptr %p) - call void @llvm.memset.p0.i64(ptr %p, i8 0, i64 8, i1 false) - call void @llvm.memcpy.p0.p0.i64(ptr @bytes, ptr %p, i64 8, i1 false) - call void @llvm.lifetime.end.p0(i64 8, ptr %p) - ret void -} - -define void @cmpxchg_expected() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %old = cmpxchg ptr @escaped_pointer, ptr %p, ptr null monotonic monotonic, align 8 - ret void -} - -@escaped_pointer = global ptr null -` - ctx, mod := transformModule(t, ir) - defer ctx.Dispose() - defer mod.Dispose() - for _, name := range []string{"load_store", "gep", "bitcast", "select_value", "phi_cycle", "compare", "atomic_addresses", "freeze_value", "volatile_addresses", "safe_intrinsics", "cmpxchg_expected"} { - requireStackAllocation(t, mod, name) - } - if got := mod.NamedFunction("load_store").String(); !strings.Contains(got, "alloca i8, i64 8, align 8") { - t.Fatalf("load alignment was not preserved:\n%s", got) - } -} - -func TestExactLocalCopy(t *testing.T) { - ir := ` -target datalayout = "e-p:64:64-i64:64-n32:64-S128" -@escaped = global ptr null -` + allocationDeclarations + ` -declare void @clobber(ptr) - -define void @exact_copy() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %slot = alloca ptr - store ptr %p, ptr %slot - %q = load ptr, ptr %slot - store i8 1, ptr %q - ret void -} - -define void @alias() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %slot = alloca ptr - %alias = getelementptr ptr, ptr %slot, i64 0 - store ptr %p, ptr %slot - %q = load ptr, ptr %alias - store i8 1, ptr %q - ret void -} - -define void @slot_address_escape() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %slot = alloca ptr - store ptr %p, ptr %slot - store ptr %slot, ptr @escaped - ret void -} - -define void @unknown_clobber() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %slot = alloca ptr - store ptr %p, ptr %slot - call void @clobber(ptr %slot) - ret void -} - -define void @unsupported_reader() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %slot = alloca ptr - store ptr %p, ptr %slot - %q = load i64, ptr %slot - ret void -} - -define void @other_store() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %slot = alloca ptr - store ptr %p, ptr %slot - store ptr null, ptr %slot - ret void -} - -define void @global_destination() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - store ptr %p, ptr @escaped - ret void -} - -define void @nonlocal_destination(ptr %dst) { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - store ptr %p, ptr %dst - ret void -} -` - ctx, mod := transformModule(t, ir) - defer ctx.Dispose() - defer mod.Dispose() - requireStackAllocation(t, mod, "exact_copy") - for _, name := range []string{"alias", "slot_address_escape", "unknown_clobber", "unsupported_reader", "other_store", "global_destination", "nonlocal_destination"} { - requireHeapAllocation(t, mod, name) - } -} - -func TestDirectCallSummariesAndRecursion(t *testing.T) { - ir := ` -target datalayout = "e-p:64:64-i64:64-n32:64-S128" -@escaped = global ptr null -` + allocationDeclarations + ` - -define void @read(ptr %p) { -entry: - %v = load i8, ptr %p - ret void -} - -define void @save(ptr %p) { -entry: - store ptr %p, ptr @escaped - ret void -} - -define ptr @identity(ptr %p) { -entry: - ret ptr %p -} - -define void @self_safe(ptr %p) { -entry: - call void @self_safe(ptr %p) - ret void -} - -define void @self_sink(ptr %p) { -entry: - call void @self_sink(ptr %p) - store ptr %p, ptr @escaped - ret void -} - -define void @mutual_safe_a(ptr %p) { -entry: - call void @mutual_safe_b(ptr %p) - ret void -} - -define void @mutual_safe_b(ptr %p) { -entry: - call void @mutual_safe_a(ptr %p) - ret void -} - -define void @mutual_sink_a(ptr %p) { -entry: - call void @mutual_sink_b(ptr %p) - ret void -} - -define void @mutual_sink_b(ptr %p) { -entry: - call void @mutual_sink_a(ptr %p) - store ptr %p, ptr @escaped - ret void -} - -define void @known_no_leak() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - call void @read(ptr %p) - ret void -} - -define void @known_heap_leak() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - call void @save(ptr %p) - ret void -} - -define void @result_local() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %q = call ptr @identity(ptr %p) - store i8 1, ptr %q - ret void -} - -define void @result_escape() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %q = call ptr @identity(ptr %p) - store ptr %q, ptr @escaped - ret void -} - -define void @direct_recursion_safe() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - call void @self_safe(ptr %p) - ret void -} - -define void @direct_recursion_sink() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - call void @self_sink(ptr %p) - ret void -} - -define void @mutual_recursion_safe() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - call void @mutual_safe_a(ptr %p) - ret void -} - -define void @mutual_recursion_sink() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - call void @mutual_sink_a(ptr %p) - ret void -} -` - ctx, mod := transformModule(t, ir) - defer ctx.Dispose() - defer mod.Dispose() - for _, name := range []string{"known_no_leak", "result_local", "direct_recursion_safe", "mutual_recursion_safe"} { - requireStackAllocation(t, mod, name) - } - for _, name := range []string{"known_heap_leak", "result_escape", "direct_recursion_sink", "mutual_recursion_sink"} { - requireHeapAllocation(t, mod, name) - } -} - -func TestUnknownCallBoundaries(t *testing.T) { - ir := ` -target datalayout = "e-p:64:64-i64:64-n32:64-S128" -` + allocationDeclarations + ` -declare void @c_function(ptr) -declare void @"github.com/goplus/llgo/runtime/internal/runtime.ChanSend"(ptr) -declare ptr @llvm.launder.invariant.group.p0(ptr) - -define void @bodyless_c() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - call void @c_function(ptr %p) - ret void -} - -define void @runtime_call() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - call void @"github.com/goplus/llgo/runtime/internal/runtime.ChanSend"(ptr %p) - ret void -} - -define void @indirect_call(ptr %fn) { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - call void %fn(ptr %p) - ret void -} - -define void @inline_asm() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - call void asm sideeffect "", "r"(ptr %p) - ret void -} - -define void @unknown_intrinsic() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %q = call ptr @llvm.launder.invariant.group.p0(ptr %p) - ret void -} -` - ctx, mod := transformModule(t, ir) - defer ctx.Dispose() - defer mod.Dispose() - for _, name := range []string{"bodyless_c", "runtime_call", "indirect_call", "inline_asm", "unknown_intrinsic"} { - requireHeapAllocation(t, mod, name) - } -} - -func TestInvokeUsesDirectSummaryAndRejectsUnknownCallee(t *testing.T) { - ir := ` -target datalayout = "e-p:64:64-i64:64-n32:64-S128" -` + allocationDeclarations + ` -declare i32 @personality(...) -declare void @external(ptr) - -define void @read_invoke(ptr %p) { -entry: - %v = load i8, ptr %p - ret void -} - -define void @known_invoke() personality ptr @personality { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - invoke void @read_invoke(ptr %p) to label %done unwind label %failed -done: - ret void -failed: - %landing = landingpad { ptr, i32 } cleanup - resume { ptr, i32 } %landing -} - -define void @unknown_invoke() personality ptr @personality { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - invoke void @external(ptr %p) to label %done unwind label %failed -done: - ret void -failed: - %landing = landingpad { ptr, i32 } cleanup - resume { ptr, i32 } %landing -} -` - ctx, mod := transformModule(t, ir) - defer ctx.Dispose() - defer mod.Dispose() - requireStackAllocation(t, mod, "known_invoke") - requireHeapAllocation(t, mod, "unknown_invoke") -} - -func TestConversionsReturnsAndAtomicPublicationEscape(t *testing.T) { - ir := ` -target datalayout = "e-p:64:64-p1:64:64-i64:64-n32:64-S128" -@escaped = global ptr null -` + allocationDeclarations + ` - -define i64 @ptr_to_int() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %n = ptrtoint ptr %p to i64 - ret i64 %n -} - -define void @address_space_cast() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %q = addrspacecast ptr %p to ptr addrspace(1) - ret void -} - -define ptr @returned() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - ret ptr %p -} - -define void @atomic_store_value() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - store atomic ptr %p, ptr @escaped release, align 8 - ret void -} - -define void @cmpxchg_replacement() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %old = cmpxchg ptr @escaped, ptr null, ptr %p monotonic monotonic, align 8 - ret void -} - -define void @atomicrmw_value() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - %old = atomicrmw xchg ptr @escaped, ptr %p monotonic, align 8 - ret void -} -` - ctx, mod := transformModule(t, ir) - defer ctx.Dispose() - defer mod.Dispose() - for _, name := range []string{"ptr_to_int", "address_space_cast", "returned", "atomic_store_value", "cmpxchg_replacement", "atomicrmw_value"} { - requireHeapAllocation(t, mod, name) - } -} - -func TestParameterSummaryFacts(t *testing.T) { - ir := ` -target datalayout = "e-p:64:64-i64:64-n32:64-S128" -@escaped = global ptr null - -define ptr @facts(ptr %p) { -entry: - %q = load ptr, ptr %p, align 8 - store ptr %q, ptr @escaped - store i8 1, ptr %p, align 4 - ret ptr %p -} - -define ptr @forward(ptr %p) { -entry: - %q = call ptr @facts(ptr %p) - ret ptr %q -} - -define void @call_parameter(ptr %fn) { -entry: - call void %fn() - ret void -} -` - ctx, mod := parseModule(t, ir) - defer ctx.Dispose() - defer mod.Dispose() - a := newAnalyzer(mod) - a.solveSummaries() - - facts := a.summaries[summaryKey{fn: mod.NamedFunction("facts"), param: 0}].summary - if facts.heapLevel != 1 || facts.mutatorLevel != 0 || facts.results[0] != 0 || facts.alignments[0] != 8 { - t.Fatalf("facts summary = %#v", facts) - } - forward := a.summaries[summaryKey{fn: mod.NamedFunction("forward"), param: 0}].summary - if forward.heapLevel != 1 || forward.mutatorLevel != 0 || forward.results[0] != 0 || forward.alignments[0] != 8 { - t.Fatalf("forward summary = %#v", forward) - } - callee := a.summaries[summaryKey{fn: mod.NamedFunction("call_parameter"), param: 0}].summary - if callee.calleeLevel != 0 || callee.heapLevel != -1 { - t.Fatalf("callee summary = %#v", callee) - } -} - -func TestAllocationRewriteAndPolicyRejections(t *testing.T) { - ir := ` -target datalayout = "e-p:64:64-i64:64-n32:64-S128" -@escaped = global ptr null -` + allocationDeclarations + ` - -define void @alloc_z() { -entry: - %p = call align 16 ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocZ"(i64 8) - store i8 1, ptr %p - ret void -} - -define void @alloc_u() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - store i8 1, ptr %p - ret void -} - -define void @escaping() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - store ptr %p, ptr @escaped - ret void -} - -define void @loop(i1 %again) { -entry: - br label %body -body: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - store i8 1, ptr %p - br i1 %again, label %body, label %exit -exit: - ret void -} - -define void @dynamic_size(i64 %size) { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 %size) - store i8 1, ptr %p - ret void -} - -define void @too_large() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 65537) - store i8 1, ptr %p - ret void -} - -define void @at_limit() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 65536) - store i8 1, ptr %p - ret void -} - -define void @zero_size() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 0) - %same = icmp eq ptr %p, null - ret void -} - -define void @excessive_alignment() { -entry: - %p = call align 131072 ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - store i8 1, ptr %p - ret void -} -` - ctx, mod := transformModule(t, ir) - defer ctx.Dispose() - defer mod.Dispose() - for _, name := range []string{"alloc_z", "alloc_u", "at_limit"} { - requireStackAllocation(t, mod, name) - } - for _, name := range []string{"escaping", "loop", "dynamic_size", "too_large", "zero_size", "excessive_alignment"} { - requireHeapAllocation(t, mod, name) - } - zeroed := mod.NamedFunction("alloc_z").String() - if !strings.Contains(zeroed, "alloca i8, i64 8, align 16") || !strings.Contains(zeroed, "call void @llvm.memset") { - t.Fatalf("AllocZ initialization/alignment was not preserved:\n%s", zeroed) - } - uninitialized := mod.NamedFunction("alloc_u").String() - if strings.Contains(uninitialized, "llvm.memset") { - t.Fatalf("AllocU was unexpectedly initialized:\n%s", uninitialized) - } -} - -func TestAllocaAddressSpaceMismatchRejectsRewrite(t *testing.T) { - ir := ` -target datalayout = "e-p:64:64-A5" -` + allocationDeclarations + ` - -define void @wrong_address_space() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - store i8 1, ptr %p - ret void -} -` - ctx, mod := transformModule(t, ir) - defer ctx.Dispose() - defer mod.Dispose() - requireHeapAllocation(t, mod, "wrong_address_space") -} - -func TestTransformPrecedesLargeAggregateAndCABILowering(t *testing.T) { - llvm.InitializeAllTargetInfos() - llvm.InitializeAllTargets() - llvm.InitializeAllTargetMCs() - ir := ` -%Large = type [65537 x i8] -` + allocationDeclarations + ` - -define void @"pkg.local"() { -entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) - store i8 1, ptr %p - ret void -} - -define %Large @"pkg.large"(ptr %src) { -entry: - %value = load %Large, ptr %src, align 1 - ret %Large %value -} -` - ctx, mod := parseModule(t, ir) - defer ctx.Dispose() - defer mod.Dispose() - prog := ssa.NewProgram(&ssa.Target{GOOS: runtime.GOOS, GOARCH: runtime.GOARCH}) - defer prog.Dispose() - mod.SetDataLayout(prog.DataLayout()) - mod.SetTarget(prog.Target().Spec().Triple) - - if err := TransformModule(mod); err != nil { - t.Fatalf("escape transform: %v", err) - } - requireStackAllocation(t, mod, "pkg.local") - llabi.LowerLargeAggregates(prog.TargetData(), mod) - cabi.NewTransformer(prog, "", "", cabi.ModeAllFunc, false).TransformModule("pkg", mod) - if err := llvm.VerifyModule(mod, llvm.ReturnStatusAction); err != nil { - t.Fatalf("module invalid after escape, large aggregate, and C ABI transforms: %v\n%s", err, mod.String()) + for _, entry := range entries { + if !entry.IsDir() { + continue + } + t.Run(entry.Name(), func(t *testing.T) { + dir := filepath.Join("testdata", entry.Name()) + input := filepath.Join(dir, "in.txt") + buffer, err := llvm.NewMemoryBufferFromFile(input) + if err != nil { + t.Fatal(err) + } + ctx := llvm.NewContext() + defer ctx.Dispose() + mod, err := ctx.ParseIR(buffer) + if err != nil { + t.Fatalf("parse %s: %v", input, err) + } + defer mod.Dispose() + if err := TransformModule(mod); err != nil { + t.Fatal(err) + } + + output := filepath.Join(dir, "out.txt") + want, err := os.ReadFile(output) + if err != nil { + t.Fatal(err) + } + spec := littest.Spec{Path: output, Text: string(want), Mode: littest.ModeLiteral} + if err := littest.Check(spec, mod.String()); err != nil { + t.Fatalf("%v\n%s", err, mod.String()) + } + }) } } diff --git a/internal/escape/testdata/address-space/in.txt b/internal/escape/testdata/address-space/in.txt new file mode 100644 index 0000000000..51fdb46b6a --- /dev/null +++ b/internal/escape/testdata/address-space/in.txt @@ -0,0 +1,10 @@ +target datalayout = "e-p:64:64-A5" + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) + +define void @wrong_address_space() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i8 1, ptr %p + ret void +} diff --git a/internal/escape/testdata/address-space/out.txt b/internal/escape/testdata/address-space/out.txt new file mode 100644 index 0000000000..8199e4256b --- /dev/null +++ b/internal/escape/testdata/address-space/out.txt @@ -0,0 +1,12 @@ +; ModuleID = 'testdata/address-space/in.txt' +source_filename = "testdata/address-space/in.txt" +target datalayout = "e-p:64:64-A5" + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) + +define void @wrong_address_space() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i8 1, ptr %p, align 1 + ret void +} diff --git a/internal/escape/testdata/calls/in.txt b/internal/escape/testdata/calls/in.txt new file mode 100644 index 0000000000..d4567676b2 --- /dev/null +++ b/internal/escape/testdata/calls/in.txt @@ -0,0 +1,158 @@ +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +@escaped = global ptr null + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) + +define void @read(ptr %p) { +entry: + %v = load i8, ptr %p, align 8 + ret void +} + +define void @write(ptr %p) { +entry: + store i8 1, ptr %p, align 8 + ret void +} + +define void @save(ptr %p) { +entry: + store ptr %p, ptr @escaped + ret void +} + +define ptr @identity(ptr %p) { +entry: + ret ptr %p +} + +define void @leak_content(ptr %p) { +entry: + %q = load ptr, ptr %p, align 8 + store ptr %q, ptr @escaped + ret void +} + +define void @self_safe(ptr %p) { +entry: + call void @self_safe(ptr %p) + ret void +} + +define void @self_sink(ptr %p) { +entry: + call void @self_sink(ptr %p) + store ptr %p, ptr @escaped + ret void +} + +define void @mutual_safe_a(ptr %p) { +entry: + call void @mutual_safe_b(ptr %p) + ret void +} + +define void @mutual_safe_b(ptr %p) { +entry: + call void @mutual_safe_a(ptr %p) + ret void +} + +define void @mutual_sink_a(ptr %p) { +entry: + call void @mutual_sink_b(ptr %p) + ret void +} + +define void @mutual_sink_b(ptr %p) { +entry: + call void @mutual_sink_a(ptr %p) + store ptr %p, ptr @escaped + ret void +} + +define void @call_parameter(ptr %fn) { +entry: + call void %fn() + ret void +} + +define void @known_no_leak() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @read(ptr %p) + ret void +} + +define void @known_heap_leak() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @save(ptr %p) + ret void +} + +define void @known_mutator() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @write(ptr %p) + ret void +} + +define void @known_content_leak() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @leak_content(ptr %p) + ret void +} + +define void @result_local() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = call ptr @identity(ptr %p) + store i8 1, ptr %q + ret void +} + +define void @result_escape() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = call ptr @identity(ptr %p) + store ptr %q, ptr @escaped + ret void +} + +define void @direct_recursion_safe() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @self_safe(ptr %p) + ret void +} + +define void @direct_recursion_sink() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @self_sink(ptr %p) + ret void +} + +define void @mutual_recursion_safe() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @mutual_safe_a(ptr %p) + ret void +} + +define void @mutual_recursion_sink() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @mutual_sink_a(ptr %p) + ret void +} + +define void @callee_effect() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @call_parameter(ptr %p) + ret void +} diff --git a/internal/escape/testdata/calls/out.txt b/internal/escape/testdata/calls/out.txt new file mode 100644 index 0000000000..deb17df093 --- /dev/null +++ b/internal/escape/testdata/calls/out.txt @@ -0,0 +1,160 @@ +; ModuleID = 'testdata/calls/in.txt' +source_filename = "testdata/calls/in.txt" +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +@escaped = global ptr null + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) + +define void @read(ptr %p) { +entry: + %v = load i8, ptr %p, align 8 + ret void +} + +define void @write(ptr %p) { +entry: + store i8 1, ptr %p, align 8 + ret void +} + +define void @save(ptr %p) { +entry: + store ptr %p, ptr @escaped, align 8 + ret void +} + +define ptr @identity(ptr %p) { +entry: + ret ptr %p +} + +define void @leak_content(ptr %p) { +entry: + %q = load ptr, ptr %p, align 8 + store ptr %q, ptr @escaped, align 8 + ret void +} + +define void @self_safe(ptr %p) { +entry: + call void @self_safe(ptr %p) + ret void +} + +define void @self_sink(ptr %p) { +entry: + call void @self_sink(ptr %p) + store ptr %p, ptr @escaped, align 8 + ret void +} + +define void @mutual_safe_a(ptr %p) { +entry: + call void @mutual_safe_b(ptr %p) + ret void +} + +define void @mutual_safe_b(ptr %p) { +entry: + call void @mutual_safe_a(ptr %p) + ret void +} + +define void @mutual_sink_a(ptr %p) { +entry: + call void @mutual_sink_b(ptr %p) + ret void +} + +define void @mutual_sink_b(ptr %p) { +entry: + call void @mutual_sink_a(ptr %p) + store ptr %p, ptr @escaped, align 8 + ret void +} + +define void @call_parameter(ptr %fn) { +entry: + call void %fn() + ret void +} + +define void @known_no_leak() { +entry: + %p.stack = alloca i8, i64 8, align 8 + call void @read(ptr %p.stack) + ret void +} + +define void @known_heap_leak() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @save(ptr %p) + ret void +} + +define void @known_mutator() { +entry: + %p.stack = alloca i8, i64 8, align 8 + call void @write(ptr %p.stack) + ret void +} + +define void @known_content_leak() { +entry: + %p.stack = alloca i8, i64 8, align 8 + call void @leak_content(ptr %p.stack) + ret void +} + +define void @result_local() { +entry: + %p.stack = alloca i8, i64 8, align 1 + %q = call ptr @identity(ptr %p.stack) + store i8 1, ptr %q, align 1 + ret void +} + +define void @result_escape() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = call ptr @identity(ptr %p) + store ptr %q, ptr @escaped, align 8 + ret void +} + +define void @direct_recursion_safe() { +entry: + %p.stack = alloca i8, i64 8, align 1 + call void @self_safe(ptr %p.stack) + ret void +} + +define void @direct_recursion_sink() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @self_sink(ptr %p) + ret void +} + +define void @mutual_recursion_safe() { +entry: + %p.stack = alloca i8, i64 8, align 1 + call void @mutual_safe_a(ptr %p.stack) + ret void +} + +define void @mutual_recursion_sink() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @mutual_sink_a(ptr %p) + ret void +} + +define void @callee_effect() { +entry: + %p.stack = alloca i8, i64 8, align 1 + call void @call_parameter(ptr %p.stack) + ret void +} diff --git a/internal/escape/testdata/escaping-uses/in.txt b/internal/escape/testdata/escaping-uses/in.txt new file mode 100644 index 0000000000..460a64ef47 --- /dev/null +++ b/internal/escape/testdata/escaping-uses/in.txt @@ -0,0 +1,46 @@ +target datalayout = "e-p:64:64-p1:64:64-i64:64-n32:64-S128" + +@escaped = global ptr null + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) + +define i64 @ptr_to_int() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %n = ptrtoint ptr %p to i64 + ret i64 %n +} + +define void @address_space_cast() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = addrspacecast ptr %p to ptr addrspace(1) + ret void +} + +define ptr @returned() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + ret ptr %p +} + +define void @atomic_store_value() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store atomic ptr %p, ptr @escaped release, align 8 + ret void +} + +define void @cmpxchg_replacement() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %old = cmpxchg ptr @escaped, ptr null, ptr %p monotonic monotonic, align 8 + ret void +} + +define void @atomicrmw_value() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %old = atomicrmw xchg ptr @escaped, ptr %p monotonic, align 8 + ret void +} diff --git a/internal/escape/testdata/escaping-uses/out.txt b/internal/escape/testdata/escaping-uses/out.txt new file mode 100644 index 0000000000..c3f8ca9318 --- /dev/null +++ b/internal/escape/testdata/escaping-uses/out.txt @@ -0,0 +1,48 @@ +; ModuleID = 'testdata/escaping-uses/in.txt' +source_filename = "testdata/escaping-uses/in.txt" +target datalayout = "e-p:64:64-p1:64:64-i64:64-n32:64-S128" + +@escaped = global ptr null + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) + +define i64 @ptr_to_int() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %n = ptrtoint ptr %p to i64 + ret i64 %n +} + +define void @address_space_cast() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = addrspacecast ptr %p to ptr addrspace(1) + ret void +} + +define ptr @returned() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + ret ptr %p +} + +define void @atomic_store_value() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store atomic ptr %p, ptr @escaped release, align 8 + ret void +} + +define void @cmpxchg_replacement() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %old = cmpxchg ptr @escaped, ptr null, ptr %p monotonic monotonic, align 8 + ret void +} + +define void @atomicrmw_value() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %old = atomicrmw xchg ptr @escaped, ptr %p monotonic, align 8 + ret void +} diff --git a/internal/escape/testdata/exact-copy/in.txt b/internal/escape/testdata/exact-copy/in.txt new file mode 100644 index 0000000000..5afbdddb6e --- /dev/null +++ b/internal/escape/testdata/exact-copy/in.txt @@ -0,0 +1,77 @@ +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +@escaped = global ptr null + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) +declare void @clobber(ptr) + +define void @exact_copy() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr %p, ptr %slot + %q = load ptr, ptr %slot + store i8 1, ptr %q + ret void +} + +define void @alias() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + %alias = getelementptr ptr, ptr %slot, i64 0 + store ptr %p, ptr %slot + %q = load ptr, ptr %alias + store i8 1, ptr %q + ret void +} + +define void @slot_address_escape() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr %p, ptr %slot + store ptr %slot, ptr @escaped + ret void +} + +define void @unknown_clobber() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr %p, ptr %slot + call void @clobber(ptr %slot) + ret void +} + +define void @unsupported_reader() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr %p, ptr %slot + %q = load i64, ptr %slot + ret void +} + +define void @other_store() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr %p, ptr %slot + store ptr null, ptr %slot + ret void +} + +define void @global_destination() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store ptr %p, ptr @escaped + ret void +} + +define void @nonlocal_destination(ptr %dst) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store ptr %p, ptr %dst + ret void +} diff --git a/internal/escape/testdata/exact-copy/out.txt b/internal/escape/testdata/exact-copy/out.txt new file mode 100644 index 0000000000..17b56e0a58 --- /dev/null +++ b/internal/escape/testdata/exact-copy/out.txt @@ -0,0 +1,80 @@ +; ModuleID = 'testdata/exact-copy/in.txt' +source_filename = "testdata/exact-copy/in.txt" +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +@escaped = global ptr null + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) + +declare void @clobber(ptr) + +define void @exact_copy() { +entry: + %p.stack = alloca i8, i64 8, align 1 + %slot = alloca ptr, align 8 + store ptr %p.stack, ptr %slot, align 8 + %q = load ptr, ptr %slot, align 8 + store i8 1, ptr %q, align 1 + ret void +} + +define void @alias() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr, align 8 + %alias = getelementptr ptr, ptr %slot, i64 0 + store ptr %p, ptr %slot, align 8 + %q = load ptr, ptr %alias, align 8 + store i8 1, ptr %q, align 1 + ret void +} + +define void @slot_address_escape() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr, align 8 + store ptr %p, ptr %slot, align 8 + store ptr %slot, ptr @escaped, align 8 + ret void +} + +define void @unknown_clobber() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr, align 8 + store ptr %p, ptr %slot, align 8 + call void @clobber(ptr %slot) + ret void +} + +define void @unsupported_reader() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr, align 8 + store ptr %p, ptr %slot, align 8 + %q = load i64, ptr %slot, align 8 + ret void +} + +define void @other_store() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr, align 8 + store ptr %p, ptr %slot, align 8 + store ptr null, ptr %slot, align 8 + ret void +} + +define void @global_destination() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store ptr %p, ptr @escaped, align 8 + ret void +} + +define void @nonlocal_destination(ptr %dst) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store ptr %p, ptr %dst, align 8 + ret void +} diff --git a/internal/escape/testdata/invoke/in.txt b/internal/escape/testdata/invoke/in.txt new file mode 100644 index 0000000000..3134bfd7e0 --- /dev/null +++ b/internal/escape/testdata/invoke/in.txt @@ -0,0 +1,33 @@ +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) +declare i32 @personality(...) +declare void @external(ptr) + +define void @read_invoke(ptr %p) { +entry: + %v = load i8, ptr %p + ret void +} + +define void @known_invoke() personality ptr @personality { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + invoke void @read_invoke(ptr %p) to label %done unwind label %failed +done: + ret void +failed: + %landing = landingpad { ptr, i32 } cleanup + resume { ptr, i32 } %landing +} + +define void @unknown_invoke() personality ptr @personality { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + invoke void @external(ptr %p) to label %done unwind label %failed +done: + ret void +failed: + %landing = landingpad { ptr, i32 } cleanup + resume { ptr, i32 } %landing +} diff --git a/internal/escape/testdata/invoke/out.txt b/internal/escape/testdata/invoke/out.txt new file mode 100644 index 0000000000..e0ec352310 --- /dev/null +++ b/internal/escape/testdata/invoke/out.txt @@ -0,0 +1,45 @@ +; ModuleID = 'testdata/invoke/in.txt' +source_filename = "testdata/invoke/in.txt" +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) + +declare i32 @personality(...) + +declare void @external(ptr) + +define void @read_invoke(ptr %p) { +entry: + %v = load i8, ptr %p, align 1 + ret void +} + +define void @known_invoke() personality ptr @personality { +entry: + %p.stack = alloca i8, i64 8, align 1 + invoke void @read_invoke(ptr %p.stack) + to label %done unwind label %failed + +done: ; preds = %entry + ret void + +failed: ; preds = %entry + %landing = landingpad { ptr, i32 } + cleanup + resume { ptr, i32 } %landing +} + +define void @unknown_invoke() personality ptr @personality { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + invoke void @external(ptr %p) + to label %done unwind label %failed + +done: ; preds = %entry + ret void + +failed: ; preds = %entry + %landing = landingpad { ptr, i32 } + cleanup + resume { ptr, i32 } %landing +} diff --git a/internal/escape/testdata/rewrite-policy/in.txt b/internal/escape/testdata/rewrite-policy/in.txt new file mode 100644 index 0000000000..22011df609 --- /dev/null +++ b/internal/escape/testdata/rewrite-policy/in.txt @@ -0,0 +1,73 @@ +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +@escaped = global ptr null + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocZ"(i64) +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) + +define void @alloc_z() { +entry: + %p = call align 16 ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocZ"(i64 8) + store i8 1, ptr %p + ret void +} + +define void @alloc_u() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i8 1, ptr %p + ret void +} + +define void @escaping() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store ptr %p, ptr @escaped + ret void +} + +define void @loop(i1 %again) { +entry: + br label %body +body: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i8 1, ptr %p + br i1 %again, label %body, label %exit +exit: + ret void +} + +define void @dynamic_size(i64 %size) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 %size) + store i8 1, ptr %p + ret void +} + +define void @too_large() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 65537) + store i8 1, ptr %p + ret void +} + +define void @at_limit() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 65536) + store i8 1, ptr %p + ret void +} + +define void @zero_size() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 0) + %same = icmp eq ptr %p, null + ret void +} + +define void @excessive_alignment() { +entry: + %p = call align 131072 ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i8 1, ptr %p + ret void +} diff --git a/internal/escape/testdata/rewrite-policy/out.txt b/internal/escape/testdata/rewrite-policy/out.txt new file mode 100644 index 0000000000..a2bd36c7eb --- /dev/null +++ b/internal/escape/testdata/rewrite-policy/out.txt @@ -0,0 +1,84 @@ +; ModuleID = 'testdata/rewrite-policy/in.txt' +source_filename = "testdata/rewrite-policy/in.txt" +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +@escaped = global ptr null + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocZ"(i64) + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) + +define void @alloc_z() { +entry: + %p.stack = alloca i8, i64 8, align 16 + call void @llvm.memset.p0.i64(ptr %p.stack, i8 0, i64 8, i1 false) + store i8 1, ptr %p.stack, align 1 + ret void +} + +define void @alloc_u() { +entry: + %p.stack = alloca i8, i64 8, align 1 + store i8 1, ptr %p.stack, align 1 + ret void +} + +define void @escaping() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store ptr %p, ptr @escaped, align 8 + ret void +} + +define void @loop(i1 %again) { +entry: + br label %body + +body: ; preds = %body, %entry + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i8 1, ptr %p, align 1 + br i1 %again, label %body, label %exit + +exit: ; preds = %body + ret void +} + +define void @dynamic_size(i64 %size) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 %size) + store i8 1, ptr %p, align 1 + ret void +} + +define void @too_large() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 65537) + store i8 1, ptr %p, align 1 + ret void +} + +define void @at_limit() { +entry: + %p.stack = alloca i8, i64 65536, align 1 + store i8 1, ptr %p.stack, align 1 + ret void +} + +define void @zero_size() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 0) + %same = icmp eq ptr %p, null + ret void +} + +define void @excessive_alignment() { +entry: + %p = call align 131072 ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i8 1, ptr %p, align 1 + ret void +} + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) +declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0 + +attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) } diff --git a/internal/escape/testdata/safe-uses/in.txt b/internal/escape/testdata/safe-uses/in.txt new file mode 100644 index 0000000000..1b21ff8fca --- /dev/null +++ b/internal/escape/testdata/safe-uses/in.txt @@ -0,0 +1,104 @@ +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) +declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) +declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) +declare void @llvm.memset.p0.i64(ptr writeonly, i8, i64, i1 immarg) +declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly, ptr noalias readonly, i64, i1 immarg) + +@bytes = global [8 x i8] zeroinitializer +@escaped_pointer = global ptr null + +define i64 @load_store() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store i64 7, ptr %p, align 8 + %v = load i64, ptr %p, align 8 + ret i64 %v +} + +define void @gep() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = getelementptr i8, ptr %p, i64 1 + store i8 7, ptr %q, align 1 + ret void +} + +define void @bitcast() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = bitcast ptr %p to ptr + store i8 7, ptr %q, align 1 + ret void +} + +define void @select_value(i1 %cond) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = select i1 %cond, ptr %p, ptr %p + store i8 7, ptr %q, align 1 + ret void +} + +define void @phi_cycle(i1 %again) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + br label %loop +loop: + %q = phi ptr [ %p, %entry ], [ %q, %loop ] + store i8 7, ptr %q, align 1 + br i1 %again, label %loop, label %exit +exit: + ret void +} + +define i1 @compare() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %same = icmp eq ptr %p, null + ret i1 %same +} + +define void @atomic_addresses() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store atomic i32 1, ptr %p monotonic, align 4 + %v = load atomic i32, ptr %p monotonic, align 4 + %cx = cmpxchg ptr %p, i32 %v, i32 2 monotonic monotonic, align 4 + %rmw = atomicrmw add ptr %p, i32 1 monotonic, align 4 + ret void +} + +define void @freeze_value() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = freeze ptr %p + store i8 7, ptr %q + ret void +} + +define void @volatile_addresses() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store volatile i8 7, ptr %p + %v = load volatile i8, ptr %p + ret void +} + +define void @safe_intrinsics() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @llvm.lifetime.start.p0(i64 8, ptr %p) + call void @llvm.memset.p0.i64(ptr %p, i8 0, i64 8, i1 false) + call void @llvm.memcpy.p0.p0.i64(ptr @bytes, ptr %p, i64 8, i1 false) + call void @llvm.lifetime.end.p0(i64 8, ptr %p) + ret void +} + +define void @cmpxchg_expected() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %old = cmpxchg ptr @escaped_pointer, ptr %p, ptr null monotonic monotonic, align 8 + ret void +} diff --git a/internal/escape/testdata/safe-uses/out.txt b/internal/escape/testdata/safe-uses/out.txt new file mode 100644 index 0000000000..6b057b6c34 --- /dev/null +++ b/internal/escape/testdata/safe-uses/out.txt @@ -0,0 +1,120 @@ +; ModuleID = 'testdata/safe-uses/in.txt' +source_filename = "testdata/safe-uses/in.txt" +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +@bytes = global [8 x i8] zeroinitializer +@escaped_pointer = global ptr null + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) + +; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0 + +; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #0 + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) +declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #1 + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #2 + +define i64 @load_store() { +entry: + %p.stack = alloca i8, i64 8, align 8 + store i64 7, ptr %p.stack, align 8 + %v = load i64, ptr %p.stack, align 8 + ret i64 %v +} + +define void @gep() { +entry: + %p.stack = alloca i8, i64 8, align 1 + %q = getelementptr i8, ptr %p.stack, i64 1 + store i8 7, ptr %q, align 1 + ret void +} + +define void @bitcast() { +entry: + %p.stack = alloca i8, i64 8, align 1 + %q = bitcast ptr %p.stack to ptr + store i8 7, ptr %q, align 1 + ret void +} + +define void @select_value(i1 %cond) { +entry: + %p.stack = alloca i8, i64 8, align 1 + %q = select i1 %cond, ptr %p.stack, ptr %p.stack + store i8 7, ptr %q, align 1 + ret void +} + +define void @phi_cycle(i1 %again) { +entry: + %p.stack = alloca i8, i64 8, align 1 + br label %loop + +loop: ; preds = %loop, %entry + %q = phi ptr [ %p.stack, %entry ], [ %q, %loop ] + store i8 7, ptr %q, align 1 + br i1 %again, label %loop, label %exit + +exit: ; preds = %loop + ret void +} + +define i1 @compare() { +entry: + %p.stack = alloca i8, i64 8, align 1 + %same = icmp eq ptr %p.stack, null + ret i1 %same +} + +define void @atomic_addresses() { +entry: + %p.stack = alloca i8, i64 8, align 4 + store atomic i32 1, ptr %p.stack monotonic, align 4 + %v = load atomic i32, ptr %p.stack monotonic, align 4 + %cx = cmpxchg ptr %p.stack, i32 %v, i32 2 monotonic monotonic, align 4 + %rmw = atomicrmw add ptr %p.stack, i32 1 monotonic, align 4 + ret void +} + +define void @freeze_value() { +entry: + %p.stack = alloca i8, i64 8, align 1 + %q = freeze ptr %p.stack + store i8 7, ptr %q, align 1 + ret void +} + +define void @volatile_addresses() { +entry: + %p.stack = alloca i8, i64 8, align 1 + store volatile i8 7, ptr %p.stack, align 1 + %v = load volatile i8, ptr %p.stack, align 1 + ret void +} + +define void @safe_intrinsics() { +entry: + %p.stack = alloca i8, i64 8, align 1 + call void @llvm.lifetime.start.p0(i64 8, ptr %p.stack) + call void @llvm.memset.p0.i64(ptr %p.stack, i8 0, i64 8, i1 false) + call void @llvm.memcpy.p0.p0.i64(ptr @bytes, ptr %p.stack, i64 8, i1 false) + call void @llvm.lifetime.end.p0(i64 8, ptr %p.stack) + ret void +} + +define void @cmpxchg_expected() { +entry: + %p.stack = alloca i8, i64 8, align 1 + %old = cmpxchg ptr @escaped_pointer, ptr %p.stack, ptr null monotonic monotonic, align 8 + ret void +} + +attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) } +attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/internal/escape/testdata/unknown-calls/in.txt b/internal/escape/testdata/unknown-calls/in.txt new file mode 100644 index 0000000000..3d3f74744d --- /dev/null +++ b/internal/escape/testdata/unknown-calls/in.txt @@ -0,0 +1,41 @@ +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) +declare void @c_function(ptr) +declare void @"github.com/goplus/llgo/runtime/internal/runtime.ChanSend"(ptr) +declare ptr @llvm.launder.invariant.group.p0(ptr) + +define void @bodyless_c() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @c_function(ptr %p) + ret void +} + +define void @runtime_call() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @"github.com/goplus/llgo/runtime/internal/runtime.ChanSend"(ptr %p) + ret void +} + +define void @indirect_call(ptr %fn) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void %fn(ptr %p) + ret void +} + +define void @inline_asm() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void asm sideeffect "", "r"(ptr %p) + ret void +} + +define void @unknown_intrinsic() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = call ptr @llvm.launder.invariant.group.p0(ptr %p) + ret void +} diff --git a/internal/escape/testdata/unknown-calls/out.txt b/internal/escape/testdata/unknown-calls/out.txt new file mode 100644 index 0000000000..6aa9970710 --- /dev/null +++ b/internal/escape/testdata/unknown-calls/out.txt @@ -0,0 +1,49 @@ +; ModuleID = 'testdata/unknown-calls/in.txt' +source_filename = "testdata/unknown-calls/in.txt" +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) + +declare void @c_function(ptr) + +declare void @"github.com/goplus/llgo/runtime/internal/runtime.ChanSend"(ptr) + +; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite) +declare ptr @llvm.launder.invariant.group.p0(ptr) #0 + +define void @bodyless_c() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @c_function(ptr %p) + ret void +} + +define void @runtime_call() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @"github.com/goplus/llgo/runtime/internal/runtime.ChanSend"(ptr %p) + ret void +} + +define void @indirect_call(ptr %fn) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void %fn(ptr %p) + ret void +} + +define void @inline_asm() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void asm sideeffect "", "r"(ptr %p) + ret void +} + +define void @unknown_intrinsic() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = call ptr @llvm.launder.invariant.group.p0(ptr %p) + ret void +} + +attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite) } From f16e860fe2cb6d7973d84c5e731173e3ec0699a6 Mon Sep 17 00:00:00 2001 From: Rick Guo Date: Sat, 1 Aug 2026 02:54:44 +0800 Subject: [PATCH 03/12] refactor: use nocapture facts for heap-to-stack --- internal/escape/escape.go | 518 +++++++++---------------- internal/escape/testdata/calls/in.txt | 99 +++++ internal/escape/testdata/calls/out.txt | 113 +++++- 3 files changed, 398 insertions(+), 332 deletions(-) diff --git a/internal/escape/escape.go b/internal/escape/escape.go index cb658a707d..b4bbb6c0cf 100644 --- a/internal/escape/escape.go +++ b/internal/escape/escape.go @@ -15,8 +15,6 @@ const ( runtimeAllocZ = runtimePrefix + "AllocZ" runtimeAllocU = runtimePrefix + "AllocU" - maxDereferenceLevel = 16 - // github.com/xgo-dev/llvm does not expose these LLVM 19 C opcode names. opcodeAtomicCmpXchg = llvm.Opcode(56) opcodeAtomicRMW = llvm.Opcode(57) @@ -24,114 +22,45 @@ const ( opcodeFreeze = llvm.Opcode(68) ) -type summaryKey struct { +type paramKey struct { fn llvm.Value param int } -type summaryState uint8 - -const ( - summaryUnseen summaryState = iota - summaryEvaluating - summaryEvaluated -) - -type parameterSummary struct { - heapLevel int - mutatorLevel int - calleeLevel int - results map[int]int - alignments map[int]int -} - -func newParameterSummary() parameterSummary { - return parameterSummary{ - heapLevel: -1, - mutatorLevel: -1, - calleeLevel: -1, - results: make(map[int]int), - alignments: make(map[int]int), - } -} - -func minLevel(dst *int, level int) bool { - if *dst >= 0 && *dst <= level { - return false - } - *dst = level - return true -} - -func (s *parameterSummary) addHeap(level int) bool { - return minLevel(&s.heapLevel, level) -} - -func (s *parameterSummary) addMutator(level int) bool { - return minLevel(&s.mutatorLevel, level) -} - -func (s *parameterSummary) addCallee(level int) bool { - return minLevel(&s.calleeLevel, level) +type locationEdge struct { + from llvm.Value + to llvm.Value + operand int } -func (s *parameterSummary) addResult(index, level int) bool { - old, ok := s.results[index] - if ok && old <= level { - return false - } - s.results[index] = level - return true +// locationGraph records diagnostic pointer flows but is never queried by the +// optimization analyses. +type locationGraph struct { + edges map[locationEdge]struct{} } -func (s *parameterSummary) addAlignment(level, align int) bool { - if align <= s.alignments[level] { - return false - } - s.alignments[level] = align - return true +func newLocationGraph() locationGraph { + return locationGraph{edges: make(map[locationEdge]struct{})} } -func (s *parameterSummary) join(src parameterSummary) bool { - changed := false - if src.heapLevel >= 0 { - changed = s.addHeap(src.heapLevel) || changed - } - if src.mutatorLevel >= 0 { - changed = s.addMutator(src.mutatorLevel) || changed - } - if src.calleeLevel >= 0 { - changed = s.addCallee(src.calleeLevel) || changed - } - for index, level := range src.results { - changed = s.addResult(index, level) || changed - } - for level, align := range src.alignments { - changed = s.addAlignment(level, align) || changed - } - return changed +func (g *locationGraph) addUse(from llvm.Value, state useState) { + g.edges[locationEdge{from: from, to: state.user, operand: state.operand}] = struct{}{} } -type summaryEntry struct { - state summaryState - summary parameterSummary - dependents map[summaryKey]struct{} +func (g *locationGraph) addFlow(from, to llvm.Value) { + g.edges[locationEdge{from: from, to: to, operand: -1}] = struct{}{} } type analyzer struct { - mod llvm.Module - - summaries map[summaryKey]*summaryEntry - summaryKeys []summaryKey - pending []summaryKey - pendingSet map[summaryKey]bool + noCapture map[paramKey]bool + noCaptureKeys []paramKey + locations locationGraph } func newAnalyzer(mod llvm.Module) *analyzer { a := &analyzer{ - mod: mod, - summaries: make(map[summaryKey]*summaryEntry), - pendingSet: make(map[summaryKey]bool), + noCapture: make(map[paramKey]bool), + locations: newLocationGraph(), } for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { if fn.IsDeclaration() || isRuntimeFunction(fn) { @@ -141,12 +70,9 @@ func newAnalyzer(mod llvm.Module) *analyzer { if !isPointer(param.Type()) { continue } - key := summaryKey{fn: fn, param: index} - a.summaries[key] = &summaryEntry{ - summary: newParameterSummary(), - dependents: make(map[summaryKey]struct{}), - } - a.summaryKeys = append(a.summaryKeys, key) + key := paramKey{fn: fn, param: index} + a.noCapture[key] = true + a.noCaptureKeys = append(a.noCaptureKeys, key) } } return a @@ -160,89 +86,57 @@ func isPointer(typ llvm.Type) bool { return typ.TypeKind() == llvm.PointerTypeKind } -func (a *analyzer) solveSummaries() { - for _, key := range a.summaryKeys { - a.evaluateSummary(key) - } - for len(a.pending) != 0 { - key := a.pending[0] - a.pending = a.pending[1:] - delete(a.pendingSet, key) - a.evaluateSummary(key) - } -} - -func (a *analyzer) enqueueSummary(key summaryKey) { - if a.pendingSet[key] { - return - } - a.pendingSet[key] = true - a.pending = append(a.pending, key) -} - -func (a *analyzer) evaluateSummary(key summaryKey) { - entry := a.summaries[key] - if entry == nil || entry.state == summaryEvaluating { - return - } - entry.state = summaryEvaluating - result := a.walk(key.fn.Param(key.param), true, key) - entry.state = summaryEvaluated - if entry.summary.join(result.summary) { - for dependent := range entry.dependents { - a.enqueueSummary(dependent) +func (a *analyzer) solveNoCapture() { + for { + changed := false + for _, key := range a.noCaptureKeys { + if a.noCapture[key] && !a.parameterNoCapture(key) { + a.noCapture[key] = false + changed = true + } + } + if !changed { + return } } } -func (a *analyzer) summaryFor(key, consumer summaryKey) (parameterSummary, bool) { - entry := a.summaries[key] - if entry == nil { - return parameterSummary{}, false - } - entry.dependents[consumer] = struct{}{} - if entry.state == summaryUnseen { - a.evaluateSummary(key) - } - return entry.summary, true -} - type useState struct { user llvm.Value operand int - derefs int } type walkResult struct { escaped bool alignment int - summary parameterSummary } -func newWalkResult(parameter bool) walkResult { - result := walkResult{} - if parameter { - result.summary = newParameterSummary() - } - return result -} - -func addUses(worklist *[]useState, value llvm.Value, derefs int) { +func (a *analyzer) addUses(worklist *[]useState, value llvm.Value) { for use := value.FirstUse(); !use.IsNil(); use = use.NextUse() { user := use.User() for operand := 0; operand < user.OperandsCount(); operand++ { if user.Operand(operand) == value { - *worklist = append(*worklist, useState{user: user, operand: operand, derefs: derefs}) + state := useState{user: user, operand: operand} + *worklist = append(*worklist, state) + a.locations.addUse(value, state) } } } } -func (a *analyzer) walk(root llvm.Value, parameter bool, consumer summaryKey) walkResult { - result := newWalkResult(parameter) +type useAction uint8 + +const ( + useSafe useAction = iota + useFollow + useCapture +) + +func (a *analyzer) checkForAllUses(root llvm.Value, classify func(useState) useAction) bool { worklist := make([]useState, 0, 8) - addUses(&worklist, root, 0) + a.addUses(&worklist, root) visited := make(map[useState]struct{}) + valid := true for len(worklist) != 0 { state := worklist[len(worklist)-1] @@ -251,108 +145,30 @@ func (a *analyzer) walk(root llvm.Value, parameter bool, consumer summaryKey) wa continue } visited[state] = struct{}{} - if state.derefs > maxDereferenceLevel { - a.recordEscape(&result, parameter, maxDereferenceLevel) - continue - } - user := state.user - opcode := user.InstructionOpcode() - switch opcode { - case llvm.Load: - a.recordAddressUse(&result, parameter, state.derefs, user.Alignment(), false) - if parameter && isPointer(user.Type()) { - addUses(&worklist, user, state.derefs+1) - } - case llvm.Store: - switch state.operand { - case 0: - copies, ok := exactLocalCopies(user) - if !ok { - a.recordEscape(&result, parameter, state.derefs) - continue - } + if state.user.InstructionOpcode() == llvm.Store && state.operand == 0 { + copies, ok := exactLocalCopies(state.user) + if ok { for _, copy := range copies { - addUses(&worklist, copy, state.derefs) + a.locations.addFlow(state.user.Operand(0), copy) + a.addUses(&worklist, copy) } - case 1: - a.recordAddressUse(&result, parameter, state.derefs, user.Alignment(), true) - default: - a.recordEscape(&result, parameter, state.derefs) - } - case llvm.GetElementPtr: - if state.operand == 0 { - addUses(&worklist, user, state.derefs) - } else { - a.recordEscape(&result, parameter, state.derefs) - } - case llvm.BitCast, llvm.PHI: - addUses(&worklist, user, state.derefs) - case llvm.Select: - if state.operand == 1 || state.operand == 2 { - addUses(&worklist, user, state.derefs) - } else { - a.recordEscape(&result, parameter, state.derefs) - } - case opcodeFreeze: - if state.operand == 0 { - addUses(&worklist, user, state.derefs) - } else { - a.recordEscape(&result, parameter, state.derefs) - } - case llvm.ICmp: - // A comparison observes pointer identity but does not retain it. - case llvm.Ret: - if parameter && state.operand == 0 { - result.summary.addResult(0, state.derefs) - } else { - a.recordEscape(&result, parameter, state.derefs) - } - case llvm.Call, llvm.Invoke: - a.handleCall(&result, &worklist, state, parameter, consumer) - case opcodeAtomicCmpXchg: - switch state.operand { - case 0: - a.recordAddressUse(&result, parameter, state.derefs, user.Alignment(), true) - case 1: - // Comparing the expected value does not publish it. - default: - a.recordEscape(&result, parameter, state.derefs) - } - case opcodeAtomicRMW: - if state.operand == 0 { - a.recordAddressUse(&result, parameter, state.derefs, user.Alignment(), true) - } else { - a.recordEscape(&result, parameter, state.derefs) + continue } - case llvm.PtrToInt, llvm.IntToPtr, opcodeAddrSpaceCast: - a.recordEscape(&result, parameter, state.derefs) - default: - a.recordEscape(&result, parameter, state.derefs) } - } - return result -} -func (a *analyzer) recordEscape(result *walkResult, parameter bool, level int) { - if parameter { - result.summary.addHeap(level) - return + switch classify(state) { + case useFollow: + a.addUses(&worklist, state.user) + case useCapture: + valid = false + } } - result.escaped = true + return valid } -func (a *analyzer) recordAddressUse(result *walkResult, parameter bool, level, align int, mutator bool) { - if parameter { - result.summary.addAlignment(level, align) - if mutator { - result.summary.addMutator(level) - } - return - } - if align > result.alignment { - result.alignment = align - } +func (a *analyzer) parameterNoCapture(key paramKey) bool { + return a.checkForAllUses(key.fn.Param(key.param), a.classifyNoCaptureUse) } func exactLocalCopies(store llvm.Value) ([]llvm.Value, bool) { @@ -397,104 +213,152 @@ func callCalleeOperand(call llvm.Value) int { return index } -func (a *analyzer) handleCall(result *walkResult, worklist *[]useState, state useState, parameter bool, consumer summaryKey) { +func (a *analyzer) classifyNoCaptureUse(state useState) useAction { + user := state.user + switch user.InstructionOpcode() { + case llvm.Call, llvm.Invoke: + if a.callArgumentNoCapture(state) { + return useSafe + } + case llvm.Load: + return useSafe + case llvm.Store: + if state.operand == 1 { + return useSafe + } + case opcodeAtomicRMW: + if state.operand == 0 { + return useSafe + } + case opcodeAtomicCmpXchg: + if state.operand == 0 || state.operand == 1 { + return useSafe + } + case llvm.VAArg: + return useSafe + case llvm.GetElementPtr: + if state.operand == 0 && user.Type().TypeKind() != llvm.VectorTypeKind { + return useFollow + } + case llvm.BitCast, llvm.PHI, opcodeAddrSpaceCast: + return useFollow + case llvm.Select: + if state.operand == 1 || state.operand == 2 { + return useFollow + } + case opcodeFreeze: + if state.operand == 0 { + return useFollow + } + case llvm.ICmp: + return useSafe + } + return useCapture +} + +func (a *analyzer) allocationUses(root llvm.Value) walkResult { + result := walkResult{} + result.escaped = !a.checkForAllUses(root, func(state useState) useAction { + user := state.user + switch user.InstructionOpcode() { + case llvm.Load: + if user.Alignment() > result.alignment { + result.alignment = user.Alignment() + } + return useSafe + case llvm.Store: + if state.operand == 1 { + if user.Alignment() > result.alignment { + result.alignment = user.Alignment() + } + return useSafe + } + case llvm.Call, llvm.Invoke: + if a.callArgumentNoCapture(state) { + return useSafe + } + case llvm.GetElementPtr: + if state.operand == 0 { + return useFollow + } + case llvm.BitCast, llvm.PHI: + return useFollow + case llvm.Select: + if state.operand == 1 || state.operand == 2 { + return useFollow + } + case opcodeFreeze: + if state.operand == 0 { + return useFollow + } + case llvm.ICmp: + return useSafe + case opcodeAtomicCmpXchg: + switch state.operand { + case 0: + if user.Alignment() > result.alignment { + result.alignment = user.Alignment() + } + return useSafe + case 1: + return useSafe + } + case opcodeAtomicRMW: + if state.operand == 0 { + if user.Alignment() > result.alignment { + result.alignment = user.Alignment() + } + return useSafe + } + } + return useCapture + }) + return result +} + +func (a *analyzer) callArgumentNoCapture(state useState) bool { call := state.user calleeOperand := callCalleeOperand(call) if state.operand == calleeOperand { - if parameter { - result.summary.addCallee(state.derefs) - } else { - result.escaped = true - } - return + return true } callee := call.CalledValue() if !callee.IsAInlineAsm().IsNil() { - a.recordEscape(result, parameter, state.derefs) - return + return false } fn := callee.IsAFunction() if fn.IsNil() { - a.recordEscape(result, parameter, state.derefs) - return + return false } if fn.IntrinsicID() != 0 { - if !a.handleIntrinsic(result, state, parameter) { - a.recordEscape(result, parameter, state.derefs) - } - return + return intrinsicArgumentNoCapture(call, fn, state.operand) } if fn.IsDeclaration() || isRuntimeFunction(fn) || state.operand >= fn.ParamsCount() { - a.recordEscape(result, parameter, state.derefs) - return + return false } - calleeSummary, ok := a.summaryFor(summaryKey{fn: fn, param: state.operand}, consumer) - if !ok { - a.recordEscape(result, parameter, state.derefs) - return - } - a.composeSummary(result, worklist, call, state.derefs, parameter, calleeSummary) + a.locations.addFlow(call.Operand(state.operand), fn.Param(state.operand)) + return a.noCapture[paramKey{fn: fn, param: state.operand}] } -func (a *analyzer) handleIntrinsic(result *walkResult, state useState, parameter bool) bool { - name := state.user.CalledValue().Name() - switch { - case strings.HasPrefix(name, "llvm.dbg."), - strings.HasPrefix(name, "llvm.lifetime.start."), - strings.HasPrefix(name, "llvm.lifetime.end."), - strings.HasPrefix(name, "llvm.objectsize."): - return true - case strings.HasPrefix(name, "llvm.memset."): - if state.operand == 0 { - a.recordAddressUse(result, parameter, state.derefs, 0, true) - return true - } - case strings.HasPrefix(name, "llvm.memcpy."), strings.HasPrefix(name, "llvm.memmove."): - if state.operand == 0 { - a.recordAddressUse(result, parameter, state.derefs, 0, true) - return true - } - if state.operand == 1 { - return true - } - } - return false -} - -func (a *analyzer) composeSummary(result *walkResult, worklist *[]useState, call llvm.Value, level int, parameter bool, summary parameterSummary) { - if summary.heapLevel >= 0 { - if parameter { - result.summary.addHeap(level + summary.heapLevel) - } else if summary.heapLevel == 0 { - result.escaped = true - } +func intrinsicArgumentNoCapture(call, fn llvm.Value, argument int) bool { + name := fn.Name() + if argument < 0 { + return false } - if parameter { - if summary.mutatorLevel >= 0 { - result.summary.addMutator(level + summary.mutatorLevel) - } - if summary.calleeLevel >= 0 { - result.summary.addCallee(level + summary.calleeLevel) - } - for derefs, align := range summary.alignments { - result.summary.addAlignment(level+derefs, align) - } - } else if align := summary.alignments[0]; align > result.alignment { - result.alignment = align + if strings.HasPrefix(name, "llvm.dbg.") || + strings.HasPrefix(name, "llvm.lifetime.start.") || + strings.HasPrefix(name, "llvm.lifetime.end.") || + strings.HasPrefix(name, "llvm.objectsize.") { + return true } - - for index, derefs := range summary.results { - if index != 0 || !isPointer(call.Type()) { - a.recordEscape(result, parameter, level) - continue - } - if !parameter && derefs != 0 { - continue - } - addUses(worklist, call, level+derefs) + kind := llvm.AttributeKindID("nocapture") + attributeIndex := argument + 1 + if attr := call.GetCallSiteEnumAttribute(attributeIndex, kind); !attr.IsNil() { + return true } + return !fn.GetEnumAttributeAtIndex(attributeIndex, kind).IsNil() } type allocationPlan struct { @@ -508,7 +372,7 @@ type allocationPlan struct { // AllocZ and AllocU calls, and verifies the resulting LLVM module. func TransformModule(mod llvm.Module) error { a := newAnalyzer(mod) - a.solveSummaries() + a.solveNoCapture() var plans []allocationPlan for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { @@ -558,7 +422,7 @@ func (a *analyzer) planAllocation(instr llvm.Value) (allocationPlan, bool) { return allocationPlan{}, false } - result := a.walk(call, false, summaryKey{}) + result := a.allocationUses(call) if result.escaped { return allocationPlan{}, false } diff --git a/internal/escape/testdata/calls/in.txt b/internal/escape/testdata/calls/in.txt index d4567676b2..ef80ea7094 100644 --- a/internal/escape/testdata/calls/in.txt +++ b/internal/escape/testdata/calls/in.txt @@ -1,8 +1,10 @@ target datalayout = "e-p:64:64-i64:64-n32:64-S128" @escaped = global ptr null +@atomic_slot = global ptr null declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) +declare void @external(ptr) define void @read(ptr %p) { entry: @@ -78,6 +80,61 @@ entry: ret void } +define void @derived(ptr %p, i1 %cond) { +entry: + %g = getelementptr i8, ptr %p, i64 1 + %b = bitcast ptr %g to ptr + %s = select i1 %cond, ptr %b, ptr %p + %f = freeze ptr %s + %same = icmp eq ptr %f, null + store volatile i8 1, ptr %f, align 1 + %v = load volatile i8, ptr %f, align 1 + ret void +} + +define void @phi_addrspace(ptr %p, i1 %cond) { +entry: + br i1 %cond, label %left, label %right +left: + %q = addrspacecast ptr %p to ptr addrspace(1) + %r = addrspacecast ptr addrspace(1) %q to ptr + br label %join +right: + br label %join +join: + %x = phi ptr [ %r, %left ], [ %p, %right ] + %v = load i8, ptr %x, align 1 + ret void +} + +define void @atomic_use(ptr %p) { +entry: + store atomic i32 1, ptr %p monotonic, align 4 + %v = load atomic i32, ptr %p monotonic, align 4 + %cx = cmpxchg ptr @atomic_slot, ptr %p, ptr null monotonic monotonic, align 8 + %rmw = atomicrmw add ptr %p, i32 1 monotonic, align 4 + ret void +} + +define void @atomic_sink(ptr %p) { +entry: + %cx = cmpxchg ptr @atomic_slot, ptr null, ptr %p monotonic monotonic, align 8 + ret void +} + +define void @unknown_param(ptr %p) { +entry: + call void @external(ptr %p) + ret void +} + +define void @recursive_load(ptr %p) { +entry: + %q = load ptr, ptr %p, align 8 + call void @recursive_load(ptr %q) + ret void +} + define void @known_no_leak() { entry: %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) @@ -156,3 +213,45 @@ entry: call void @call_parameter(ptr %p) ret void } + +define void @known_derived(i1 %cond) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @derived(ptr %p, i1 %cond) + ret void +} + +define void @known_phi_addrspace(i1 %cond) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @phi_addrspace(ptr %p, i1 %cond) + ret void +} + +define void @known_atomic() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @atomic_use(ptr %p) + ret void +} + +define void @known_atomic_sink() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @atomic_sink(ptr %p) + ret void +} + +define void @known_unknown_param() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @unknown_param(ptr %p) + ret void +} + +define void @recursive_alignment() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @recursive_load(ptr %p) + ret void +} diff --git a/internal/escape/testdata/calls/out.txt b/internal/escape/testdata/calls/out.txt index deb17df093..affbc3632d 100644 --- a/internal/escape/testdata/calls/out.txt +++ b/internal/escape/testdata/calls/out.txt @@ -3,9 +3,12 @@ source_filename = "testdata/calls/in.txt" target datalayout = "e-p:64:64-i64:64-n32:64-S128" @escaped = global ptr null +@atomic_slot = global ptr null declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) +declare void @external(ptr) + define void @read(ptr %p) { entry: %v = load i8, ptr %p, align 8 @@ -80,9 +83,67 @@ entry: ret void } +define void @derived(ptr %p, i1 %cond) { +entry: + %g = getelementptr i8, ptr %p, i64 1 + %b = bitcast ptr %g to ptr + %s = select i1 %cond, ptr %b, ptr %p + %f = freeze ptr %s + %same = icmp eq ptr %f, null + store volatile i8 1, ptr %f, align 1 + %v = load volatile i8, ptr %f, align 1 + ret void +} + +define void @phi_addrspace(ptr %p, i1 %cond) { +entry: + br i1 %cond, label %left, label %right + +left: ; preds = %entry + %q = addrspacecast ptr %p to ptr addrspace(1) + %r = addrspacecast ptr addrspace(1) %q to ptr + br label %join + +right: ; preds = %entry + br label %join + +join: ; preds = %right, %left + %x = phi ptr [ %r, %left ], [ %p, %right ] + %v = load i8, ptr %x, align 1 + ret void +} + +define void @atomic_use(ptr %p) { +entry: + store atomic i32 1, ptr %p monotonic, align 4 + %v = load atomic i32, ptr %p monotonic, align 4 + %cx = cmpxchg ptr @atomic_slot, ptr %p, ptr null monotonic monotonic, align 8 + %rmw = atomicrmw add ptr %p, i32 1 monotonic, align 4 + ret void +} + +define void @atomic_sink(ptr %p) { +entry: + %cx = cmpxchg ptr @atomic_slot, ptr null, ptr %p monotonic monotonic, align 8 + ret void +} + +define void @unknown_param(ptr %p) { +entry: + call void @external(ptr %p) + ret void +} + +define void @recursive_load(ptr %p) { +entry: + %q = load ptr, ptr %p, align 8 + call void @recursive_load(ptr %q) + ret void +} + define void @known_no_leak() { entry: - %p.stack = alloca i8, i64 8, align 8 + %p.stack = alloca i8, i64 8, align 1 call void @read(ptr %p.stack) ret void } @@ -96,22 +157,22 @@ entry: define void @known_mutator() { entry: - %p.stack = alloca i8, i64 8, align 8 + %p.stack = alloca i8, i64 8, align 1 call void @write(ptr %p.stack) ret void } define void @known_content_leak() { entry: - %p.stack = alloca i8, i64 8, align 8 + %p.stack = alloca i8, i64 8, align 1 call void @leak_content(ptr %p.stack) ret void } define void @result_local() { entry: - %p.stack = alloca i8, i64 8, align 1 - %q = call ptr @identity(ptr %p.stack) + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %q = call ptr @identity(ptr %p) store i8 1, ptr %q, align 1 ret void } @@ -158,3 +219,45 @@ entry: call void @call_parameter(ptr %p.stack) ret void } + +define void @known_derived(i1 %cond) { +entry: + %p.stack = alloca i8, i64 8, align 1 + call void @derived(ptr %p.stack, i1 %cond) + ret void +} + +define void @known_phi_addrspace(i1 %cond) { +entry: + %p.stack = alloca i8, i64 8, align 1 + call void @phi_addrspace(ptr %p.stack, i1 %cond) + ret void +} + +define void @known_atomic() { +entry: + %p.stack = alloca i8, i64 8, align 1 + call void @atomic_use(ptr %p.stack) + ret void +} + +define void @known_atomic_sink() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @atomic_sink(ptr %p) + ret void +} + +define void @known_unknown_param() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @unknown_param(ptr %p) + ret void +} + +define void @recursive_alignment() { +entry: + %p.stack = alloca i8, i64 8, align 1 + call void @recursive_load(ptr %p.stack) + ret void +} From c133310a046102874156127e073d02017b317915 Mon Sep 17 00:00:00 2001 From: Rick Guo Date: Sat, 1 Aug 2026 12:07:26 +0800 Subject: [PATCH 04/12] fix: defer module verification until LLVM passes --- internal/escape/escape.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/internal/escape/escape.go b/internal/escape/escape.go index b4bbb6c0cf..e6118ea242 100644 --- a/internal/escape/escape.go +++ b/internal/escape/escape.go @@ -3,7 +3,6 @@ package escape import ( - "fmt" "strings" llabi "github.com/goplus/llgo/internal/abi" @@ -368,8 +367,8 @@ type allocationPlan struct { alignment int } -// TransformModule analyzes eligible LLGo allocations, rewrites proven-local -// AllocZ and AllocU calls, and verifies the resulting LLVM module. +// TransformModule analyzes eligible LLGo allocations and rewrites proven-local +// AllocZ and AllocU calls. func TransformModule(mod llvm.Module) error { a := newAnalyzer(mod) a.solveNoCapture() @@ -392,11 +391,6 @@ func TransformModule(mod llvm.Module) error { for _, plan := range plans { rewriteAllocation(mod.Context(), plan) } - if len(plans) != 0 { - if err := llvm.VerifyModule(mod, llvm.ReturnStatusAction); err != nil { - return fmt.Errorf("verify heap-to-stack transform: %w", err) - } - } return nil } From 2e7a2e74e695f17d48b929313fda21e484d631e8 Mon Sep 17 00:00:00 2001 From: Rick Guo Date: Sat, 1 Aug 2026 12:17:32 +0800 Subject: [PATCH 05/12] fix: preserve alignment across nocapture calls --- internal/escape/escape.go | 73 ++++++++++++++++++++++++-- internal/escape/testdata/calls/out.txt | 10 ++-- 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/internal/escape/escape.go b/internal/escape/escape.go index e6118ea242..a747bffa75 100644 --- a/internal/escape/escape.go +++ b/internal/escape/escape.go @@ -212,6 +212,14 @@ func callCalleeOperand(call llvm.Value) int { return index } +func definedCallParameter(call llvm.Value, argument int) (paramKey, bool) { + fn := call.CalledValue().IsAFunction() + if fn.IsNil() || fn.IntrinsicID() != 0 || fn.IsDeclaration() || isRuntimeFunction(fn) || argument >= fn.ParamsCount() { + return paramKey{}, false + } + return paramKey{fn: fn, param: argument}, true +} + func (a *analyzer) classifyNoCaptureUse(state useState) useAction { user := state.user switch user.InstructionOpcode() { @@ -255,6 +263,59 @@ func (a *analyzer) classifyNoCaptureUse(state useState) useAction { return useCapture } +func (a *analyzer) requiredAlignment(root paramKey) int { + alignment := 0 + worklist := []paramKey{root} + visited := make(map[paramKey]struct{}) + + for len(worklist) != 0 { + key := worklist[len(worklist)-1] + worklist = worklist[:len(worklist)-1] + if _, ok := visited[key]; ok { + continue + } + visited[key] = struct{}{} + + a.checkForAllUses(key.fn.Param(key.param), func(state useState) useAction { + user := state.user + switch user.InstructionOpcode() { + case llvm.Load: + if user.Alignment() > alignment { + alignment = user.Alignment() + } + case llvm.Store: + if state.operand == 1 && user.Alignment() > alignment { + alignment = user.Alignment() + } + case llvm.Call, llvm.Invoke: + if callee, ok := definedCallParameter(user, state.operand); ok && a.noCapture[callee] { + worklist = append(worklist, callee) + } + case llvm.GetElementPtr: + if state.operand == 0 { + return useFollow + } + case llvm.BitCast, llvm.PHI, opcodeAddrSpaceCast: + return useFollow + case llvm.Select: + if state.operand == 1 || state.operand == 2 { + return useFollow + } + case opcodeFreeze: + if state.operand == 0 { + return useFollow + } + case opcodeAtomicCmpXchg, opcodeAtomicRMW: + if state.operand == 0 && user.Alignment() > alignment { + alignment = user.Alignment() + } + } + return useSafe + }) + } + return alignment +} + func (a *analyzer) allocationUses(root llvm.Value) walkResult { result := walkResult{} result.escaped = !a.checkForAllUses(root, func(state useState) useAction { @@ -274,6 +335,11 @@ func (a *analyzer) allocationUses(root llvm.Value) walkResult { } case llvm.Call, llvm.Invoke: if a.callArgumentNoCapture(state) { + if key, ok := definedCallParameter(user, state.operand); ok { + if alignment := a.requiredAlignment(key); alignment > result.alignment { + result.alignment = alignment + } + } return useSafe } case llvm.GetElementPtr: @@ -333,12 +399,13 @@ func (a *analyzer) callArgumentNoCapture(state useState) bool { if fn.IntrinsicID() != 0 { return intrinsicArgumentNoCapture(call, fn, state.operand) } - if fn.IsDeclaration() || isRuntimeFunction(fn) || state.operand >= fn.ParamsCount() { + key, ok := definedCallParameter(call, state.operand) + if !ok { return false } - a.locations.addFlow(call.Operand(state.operand), fn.Param(state.operand)) - return a.noCapture[paramKey{fn: fn, param: state.operand}] + a.locations.addFlow(call.Operand(state.operand), key.fn.Param(key.param)) + return a.noCapture[key] } func intrinsicArgumentNoCapture(call, fn llvm.Value, argument int) bool { diff --git a/internal/escape/testdata/calls/out.txt b/internal/escape/testdata/calls/out.txt index affbc3632d..69b87ac00a 100644 --- a/internal/escape/testdata/calls/out.txt +++ b/internal/escape/testdata/calls/out.txt @@ -143,7 +143,7 @@ entry: define void @known_no_leak() { entry: - %p.stack = alloca i8, i64 8, align 1 + %p.stack = alloca i8, i64 8, align 8 call void @read(ptr %p.stack) ret void } @@ -157,14 +157,14 @@ entry: define void @known_mutator() { entry: - %p.stack = alloca i8, i64 8, align 1 + %p.stack = alloca i8, i64 8, align 8 call void @write(ptr %p.stack) ret void } define void @known_content_leak() { entry: - %p.stack = alloca i8, i64 8, align 1 + %p.stack = alloca i8, i64 8, align 8 call void @leak_content(ptr %p.stack) ret void } @@ -236,7 +236,7 @@ entry: define void @known_atomic() { entry: - %p.stack = alloca i8, i64 8, align 1 + %p.stack = alloca i8, i64 8, align 4 call void @atomic_use(ptr %p.stack) ret void } @@ -257,7 +257,7 @@ entry: define void @recursive_alignment() { entry: - %p.stack = alloca i8, i64 8, align 1 + %p.stack = alloca i8, i64 8, align 8 call void @recursive_load(ptr %p.stack) ret void } From 5d9815f51681debe7cb9e031ece49a74fadacf70 Mon Sep 17 00:00:00 2001 From: Rick Guo Date: Sat, 1 Aug 2026 14:22:47 +0800 Subject: [PATCH 06/12] test: update heap-to-stack LITTEST checks --- cl/_testdata/print/in.go | 33 +++--- cl/_testgo/alias/in.go | 10 +- cl/_testgo/cgobasic/cgobasic.go | 66 ++++++------ cl/_testgo/cgocfiles/cgocfiles.go | 11 +- cl/_testgo/cgodefer/cgodefer.go | 11 +- cl/_testgo/cgomacro/cgomacro.go | 58 ++++++----- cl/_testgo/cgopython/cgopython.go | 11 +- cl/_testgo/cursor/in.go | 9 +- cl/_testgo/returnorder/in.go | 17 ++-- cl/_testgo/tprecurfn/in.go | 27 ++--- cl/_testgo/tptypes/in.go | 161 +++++++++++++++--------------- cl/_testlibc/atomic/in.go | 12 +-- cl/_testlibgo/atomic/in.go | 28 +++--- cl/_testrt/abitype/in.go | 59 +++++------ cl/_testrt/builtin/in.go | 9 +- cl/_testrt/eface/in.go | 11 +- cl/_testrt/funcaddr/in.go | 32 +++--- cl/_testrt/funcdecl/in.go | 15 +-- cl/_testrt/index/in.go | 55 +++++----- cl/_testrt/len/in.go | 148 +++++++++++++-------------- cl/_testrt/tpunsafe/in.go | 10 +- cl/_testrt/typalias/in.go | 13 +-- cl/_testrt/vamethod/in.go | 93 ++++++++--------- 23 files changed, 467 insertions(+), 432 deletions(-) diff --git a/cl/_testdata/print/in.go b/cl/_testdata/print/in.go index b934f989bc..ad97ff65d7 100644 --- a/cl/_testdata/print/in.go +++ b/cl/_testdata/print/in.go @@ -40,22 +40,23 @@ type stringStruct struct { // CHECK-NEXT: _llgo_0: // CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) // CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" %0, ptr %1, align 8 -// CHECK-NEXT: %2 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) -// CHECK-NEXT: %3 = call ptr @main.stringStructOf(ptr %1) -// CHECK-NEXT: %4 = getelementptr inbounds %main.stringStruct, ptr %3, i32 0, i32 0 -// CHECK-NEXT: %5 = load ptr, ptr %4, align 8 -// CHECK-NEXT: %6 = getelementptr inbounds %main.slice, ptr %2, i32 0, i32 0 -// CHECK-NEXT: store ptr %5, ptr %6, align 8 -// CHECK-NEXT: %7 = getelementptr inbounds %main.stringStruct, ptr %3, i32 0, i32 1 -// CHECK-NEXT: %8 = load i64, ptr %7, align 8 -// CHECK-NEXT: %9 = getelementptr inbounds %main.slice, ptr %2, i32 0, i32 1 -// CHECK-NEXT: store i64 %8, ptr %9, align 8 -// CHECK-NEXT: %10 = getelementptr inbounds %main.stringStruct, ptr %3, i32 0, i32 1 -// CHECK-NEXT: %11 = load i64, ptr %10, align 8 -// CHECK-NEXT: %12 = getelementptr inbounds %main.slice, ptr %2, i32 0, i32 2 -// CHECK-NEXT: store i64 %11, ptr %12, align 8 -// CHECK-NEXT: %13 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %2, align 8 -// CHECK-NEXT: ret %"{{.*}}/runtime/internal/runtime.Slice" %13 +// CHECK-NEXT: %.stack = alloca i8, i64 24, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) +// CHECK-NEXT: %2 = call ptr @main.stringStructOf(ptr %1) +// CHECK-NEXT: %3 = getelementptr inbounds %main.stringStruct, ptr %2, i32 0, i32 0 +// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 +// CHECK-NEXT: %5 = getelementptr inbounds %main.slice, ptr %.stack, i32 0, i32 0 +// CHECK-NEXT: store ptr %4, ptr %5, align 8 +// CHECK-NEXT: %6 = getelementptr inbounds %main.stringStruct, ptr %2, i32 0, i32 1 +// CHECK-NEXT: %7 = load i64, ptr %6, align 8 +// CHECK-NEXT: %8 = getelementptr inbounds %main.slice, ptr %.stack, i32 0, i32 1 +// CHECK-NEXT: store i64 %7, ptr %8, align 8 +// CHECK-NEXT: %9 = getelementptr inbounds %main.stringStruct, ptr %2, i32 0, i32 1 +// CHECK-NEXT: %10 = load i64, ptr %9, align 8 +// CHECK-NEXT: %11 = getelementptr inbounds %main.slice, ptr %.stack, i32 0, i32 2 +// CHECK-NEXT: store i64 %10, ptr %11, align 8 +// CHECK-NEXT: %12 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %.stack, align 8 +// CHECK-NEXT: ret %"{{.*}}/runtime/internal/runtime.Slice" %12 // CHECK-NEXT: } func bytes(s string) (ret []byte) { diff --git a/cl/_testgo/alias/in.go b/cl/_testgo/alias/in.go index 20d91abba6..9c24794a0e 100644 --- a/cl/_testgo/alias/in.go +++ b/cl/_testgo/alias/in.go @@ -46,12 +46,12 @@ func (p *Point) Scale(factor float64) { // CHECK-LABEL: define void @main.main(){{.*}} { func main() { - // CHECK: call ptr @"{{.*}}AllocZ"(i64 16) - // CHECK: call void @"main.(*Point).Scale"(ptr %0, double 2.000000e+00) - // CHECK: call void @"main.(*Point).Move"(ptr %0, double 3.000000e+00, double 4.000000e+00) - // CHECK: call void @"{{.*}}PrintFloat"(double %4) + // CHECK: %.stack = alloca i8, i64 16, align 8 + // CHECK: call void @"main.(*Point).Scale"(ptr %.stack, double 2.000000e+00) + // CHECK: call void @"main.(*Point).Move"(ptr %.stack, double 3.000000e+00, double 4.000000e+00) + // CHECK: call void @"{{.*}}PrintFloat"(double %3) // CHECK-NEXT: call void @"{{.*}}PrintByte"(i8 32) - // CHECK-NEXT: call void @"{{.*}}PrintFloat"(double %6) + // CHECK-NEXT: call void @"{{.*}}PrintFloat"(double %5) // CHECK-NEXT: call void @"{{.*}}PrintByte"(i8 10) // CHECK-NEXT: ret void pt := &MyPoint{1, 2} diff --git a/cl/_testgo/cgobasic/cgobasic.go b/cl/_testgo/cgobasic/cgobasic.go index 8017d92376..cb214c23ba 100644 --- a/cl/_testgo/cgobasic/cgobasic.go +++ b/cl/_testgo/cgobasic/cgobasic.go @@ -14,56 +14,62 @@ import ( // CHECK-LABEL: define double @main._Cfunc_cos(double %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_cos, align 8 -// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 -// CHECK-NEXT: %4 = call double %3(double %0) -// CHECK-NEXT: ret double %4 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_cos, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %3 = call double %2(double %0) +// CHECK-NEXT: ret double %3 // CHECK-NEXT: } // CHECK-LABEL: define [0 x i8] @main._Cfunc_free(ptr %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 -// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 -// CHECK-NEXT: %4 = call [0 x i8] %3(ptr %0) -// CHECK-NEXT: ret [0 x i8] %4 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %3 = call [0 x i8] %2(ptr %0) +// CHECK-NEXT: ret [0 x i8] %3 // CHECK-NEXT: } // CHECK-LABEL: define double @main._Cfunc_log(double %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_log, align 8 -// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 -// CHECK-NEXT: %4 = call double %3(double %0) -// CHECK-NEXT: ret double %4 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_log, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %3 = call double %2(double %0) +// CHECK-NEXT: ret double %3 // CHECK-NEXT: } // CHECK-LABEL: define i32 @main._Cfunc_puts(ptr %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_puts, align 8 -// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 -// CHECK-NEXT: %4 = call i32 %3(ptr %0) -// CHECK-NEXT: ret i32 %4 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_puts, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %3 = call i32 %2(ptr %0) +// CHECK-NEXT: ret i32 %3 // CHECK-NEXT: } // CHECK-LABEL: define double @main._Cfunc_sin(double %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sin, align 8 -// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 -// CHECK-NEXT: %4 = call double %3(double %0) -// CHECK-NEXT: ret double %4 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sin, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %3 = call double %2(double %0) +// CHECK-NEXT: ret double %3 // CHECK-NEXT: } // CHECK-LABEL: define double @main._Cfunc_sqrt(double %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sqrt, align 8 -// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 -// CHECK-NEXT: %4 = call double %3(double %0) -// CHECK-NEXT: ret double %4 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sqrt, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %3 = call double %2(double %0) +// CHECK-NEXT: ret double %3 // CHECK-NEXT: } // CHECK-LABEL: define void @main.init(){{.*}} { diff --git a/cl/_testgo/cgocfiles/cgocfiles.go b/cl/_testgo/cgocfiles/cgocfiles.go index 51d85ab1e8..5f346d275c 100644 --- a/cl/_testgo/cgocfiles/cgocfiles.go +++ b/cl/_testgo/cgocfiles/cgocfiles.go @@ -9,11 +9,12 @@ import "fmt" // CHECK-LABEL: define i32 @main._Cfunc_test_structs(ptr %0, ptr %1, ptr %2, ptr %3, ptr %4){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %5 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %6 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_test_structs, align 8 -// CHECK-NEXT: %7 = load ptr, ptr %6, align 8 -// CHECK-NEXT: %8 = call i32 %7(ptr %0, ptr %1, ptr %2, ptr %3, ptr %4) -// CHECK-NEXT: ret i32 %8 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %5 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_test_structs, align 8 +// CHECK-NEXT: %6 = load ptr, ptr %5, align 8 +// CHECK-NEXT: %7 = call i32 %6(ptr %0, ptr %1, ptr %2, ptr %3, ptr %4) +// CHECK-NEXT: ret i32 %7 // CHECK-NEXT: } // CHECK-LABEL: define void @main.main(){{.*}} { diff --git a/cl/_testgo/cgodefer/cgodefer.go b/cl/_testgo/cgodefer/cgodefer.go index 8d9bca1306..ee04924ebf 100644 --- a/cl/_testgo/cgodefer/cgodefer.go +++ b/cl/_testgo/cgodefer/cgodefer.go @@ -8,11 +8,12 @@ import "C" // CHECK-LABEL: define [0 x i8] @main._Cfunc_free(ptr %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 -// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 -// CHECK-NEXT: %4 = call [0 x i8] %3(ptr %0) -// CHECK-NEXT: ret [0 x i8] %4 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %3 = call [0 x i8] %2(ptr %0) +// CHECK-NEXT: ret [0 x i8] %3 // CHECK-NEXT: } // CHECK-LABEL: define void @main.main(){{.*}} { diff --git a/cl/_testgo/cgomacro/cgomacro.go b/cl/_testgo/cgomacro/cgomacro.go index c2c152d37d..701ab9b7e5 100644 --- a/cl/_testgo/cgomacro/cgomacro.go +++ b/cl/_testgo/cgomacro/cgomacro.go @@ -20,11 +20,12 @@ import ( // CHECK-LABEL: define i32 @main._Cfunc_PyObject_Print(ptr %0, ptr %1, i32 %2){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %3 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %4 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyObject_Print, align 8 -// CHECK-NEXT: %5 = load ptr, ptr %4, align 8 -// CHECK-NEXT: %6 = call i32 %5(ptr %0, ptr %1, i32 %2) -// CHECK-NEXT: ret i32 %6 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %3 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyObject_Print, align 8 +// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 +// CHECK-NEXT: %5 = call i32 %4(ptr %0, ptr %1, i32 %2) +// CHECK-NEXT: ret i32 %5 // CHECK-NEXT: } // CHECK-LABEL: define [0 x i8] @main._Cfunc_Py_Finalize(){{.*}} { @@ -45,11 +46,12 @@ import ( // CHECK-LABEL: define i32 @main._Cfunc_fputs(ptr %0, ptr %1){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %2 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %3 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_fputs, align 8 -// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 -// CHECK-NEXT: %5 = call i32 %4(ptr %0, ptr %1) -// CHECK-NEXT: ret i32 %5 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_fputs, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 +// CHECK-NEXT: %4 = call i32 %3(ptr %0, ptr %1) +// CHECK-NEXT: ret i32 %4 // CHECK-NEXT: } // CHECK-LABEL: define [0 x i8] @main._Cfunc_test_stdout(){{.*}} { @@ -62,34 +64,38 @@ import ( // CHECK-LABEL: define ptr @main._Cmacro_Py_False(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_False, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: ret ptr %2 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_False, align 8 +// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 +// CHECK-NEXT: ret ptr %1 // CHECK-NEXT: } // CHECK-LABEL: define ptr @main._Cmacro_Py_None(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_None, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: ret ptr %2 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_None, align 8 +// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 +// CHECK-NEXT: ret ptr %1 // CHECK-NEXT: } // CHECK-LABEL: define ptr @main._Cmacro_Py_True(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_True, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: ret ptr %2 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_True, align 8 +// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 +// CHECK-NEXT: ret ptr %1 // CHECK-NEXT: } // CHECK-LABEL: define ptr @main._Cmacro_stdout(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_stdout, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: ret ptr %2 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_stdout, align 8 +// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 +// CHECK-NEXT: ret ptr %1 // CHECK-NEXT: } // CHECK-LABEL: define void @main.init(){{.*}} { diff --git a/cl/_testgo/cgopython/cgopython.go b/cl/_testgo/cgopython/cgopython.go index aea2a0f615..d3c756558f 100644 --- a/cl/_testgo/cgopython/cgopython.go +++ b/cl/_testgo/cgopython/cgopython.go @@ -9,11 +9,12 @@ import "C" // CHECK-LABEL: define i32 @main._Cfunc_PyRun_SimpleString(ptr %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyRun_SimpleString, align 8 -// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 -// CHECK-NEXT: %4 = call i32 %3(ptr %0) -// CHECK-NEXT: ret i32 %4 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyRun_SimpleString, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %3 = call i32 %2(ptr %0) +// CHECK-NEXT: ret i32 %3 // CHECK-NEXT: } // CHECK-LABEL: define [0 x i8] @main._Cfunc_Py_Finalize(){{.*}} { diff --git a/cl/_testgo/cursor/in.go b/cl/_testgo/cursor/in.go index d0a2fc24bf..935e2182a3 100644 --- a/cl/_testgo/cursor/in.go +++ b/cl/_testgo/cursor/in.go @@ -798,10 +798,11 @@ const ( // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) -// CHECK-NEXT: %1 = getelementptr inbounds %main.Cursor, ptr %0, i32 0, i32 0 -// CHECK-NEXT: %2 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) -// CHECK-NEXT: store ptr %2, ptr %1, align 8 +// CHECK-NEXT: %.stack = alloca i8, i64 16, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// CHECK-NEXT: %0 = getelementptr inbounds %main.Cursor, ptr %.stack, i32 0, i32 0 +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// CHECK-NEXT: store ptr %1, ptr %0, align 8 // CHECK-NEXT: ret void // CHECK-NEXT: } diff --git a/cl/_testgo/returnorder/in.go b/cl/_testgo/returnorder/in.go index de98fd2b9b..a3919d1efc 100644 --- a/cl/_testgo/returnorder/in.go +++ b/cl/_testgo/returnorder/in.go @@ -28,14 +28,15 @@ func main() { // CHECK-LABEL: define { %main.state, i64 } @main.returnStateAndMut(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %0 = call ptr @"{{.*}}AllocZ"(i64 8) -// CHECK-NEXT: %1 = getelementptr inbounds %main.state, ptr %0, i32 0, i32 0 -// CHECK-NEXT: store i64 1, ptr %1, align 8 -// CHECK-NEXT: %2 = call i64 @"main.(*state).mutate"(ptr %0, i64 2) -// CHECK-NEXT: %3 = load %main.state, ptr %0, align 8 -// CHECK-NEXT: %4 = insertvalue { %main.state, i64 } undef, %main.state %3, 0 -// CHECK-NEXT: %5 = insertvalue { %main.state, i64 } %4, i64 %2, 1 -// CHECK-NEXT: ret { %main.state, i64 } %5 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %0 = getelementptr inbounds %main.state, ptr %.stack, i32 0, i32 0 +// CHECK-NEXT: store i64 1, ptr %0, align 8 +// CHECK-NEXT: %1 = call i64 @"main.(*state).mutate"(ptr %.stack, i64 2) +// CHECK-NEXT: %2 = load %main.state, ptr %.stack, align 8 +// CHECK-NEXT: %3 = insertvalue { %main.state, i64 } undef, %main.state %2, 0 +// CHECK-NEXT: %4 = insertvalue { %main.state, i64 } %3, i64 %1, 1 +// CHECK-NEXT: ret { %main.state, i64 } %4 func returnStateAndMut() (state, int) { x := state{v: 1} return x, x.mutate(2) diff --git a/cl/_testgo/tprecurfn/in.go b/cl/_testgo/tprecurfn/in.go index de9928916d..08c3ae7f8e 100644 --- a/cl/_testgo/tprecurfn/in.go +++ b/cl/_testgo/tprecurfn/in.go @@ -8,19 +8,20 @@ type My[T any] struct { // CHECK-LABEL: define void @main.main(){{.*}} { func main() { - // CHECK: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) - // CHECK-NEXT: %1 = getelementptr inbounds %"main.My[int]", ptr %0, i32 0, i32 1 - // CHECK-NEXT: %2 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) - // CHECK-NEXT: %3 = getelementptr inbounds %"main.My[int]", ptr %2, i32 0, i32 0 - // CHECK-NEXT: store { ptr, ptr } { ptr @"__llgo_stub.main.main$1", ptr null }, ptr %3, align 8 - // CHECK-NEXT: store ptr %2, ptr %1, align 8 - // CHECK-NEXT: %4 = getelementptr inbounds %"main.My[int]", ptr %0, i32 0, i32 1 - // CHECK-NEXT: %5 = load ptr, ptr %4, align 8 - // CHECK-NEXT: %6 = getelementptr inbounds %"main.My[int]", ptr %5, i32 0, i32 0 - // CHECK-NEXT: %7 = load { ptr, ptr }, ptr %6, align 8 - // CHECK-NEXT: %8 = extractvalue { ptr, ptr } %7, 1 - // CHECK-NEXT: %9 = extractvalue { ptr, ptr } %7, 0 - // CHECK-NEXT: call void %9(ptr %8, i64 100) + // CHECK: %.stack = alloca i8, i64 24, align 8 + // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) + // CHECK-NEXT: %0 = getelementptr inbounds %"main.My[int]", ptr %.stack, i32 0, i32 1 + // CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) + // CHECK-NEXT: %2 = getelementptr inbounds %"main.My[int]", ptr %1, i32 0, i32 0 + // CHECK-NEXT: store { ptr, ptr } { ptr @"__llgo_stub.main.main$1", ptr null }, ptr %2, align 8 + // CHECK-NEXT: store ptr %1, ptr %0, align 8 + // CHECK-NEXT: %3 = getelementptr inbounds %"main.My[int]", ptr %.stack, i32 0, i32 1 + // CHECK-NEXT: %4 = load ptr, ptr %3, align 8 + // CHECK-NEXT: %5 = getelementptr inbounds %"main.My[int]", ptr %4, i32 0, i32 0 + // CHECK-NEXT: %6 = load { ptr, ptr }, ptr %5, align 8 + // CHECK-NEXT: %7 = extractvalue { ptr, ptr } %6, 1 + // CHECK-NEXT: %8 = extractvalue { ptr, ptr } %6, 0 + // CHECK-NEXT: call void %8(ptr %7, i64 100) // CHECK-NEXT: ret void // CHECK-NEXT:} m := &My[int]{next: &My[int]{fn: func(n int) { println(n) }}} diff --git a/cl/_testgo/tptypes/in.go b/cl/_testgo/tptypes/in.go index c6130848bd..b300c773a1 100644 --- a/cl/_testgo/tptypes/in.go +++ b/cl/_testgo/tptypes/in.go @@ -89,90 +89,93 @@ type ( // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 0) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %16 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) -// CHECK-NEXT: %17 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %18 = getelementptr inbounds i64, ptr %17, i64 0 -// CHECK-NEXT: store i64 100, ptr %18, align 8 -// CHECK-NEXT: %19 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %17, 0 -// CHECK-NEXT: %20 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %19, i64 1, 1 -// CHECK-NEXT: %21 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %20, i64 1, 2 -// CHECK-NEXT: %22 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %16, %"{{.*}}/runtime/internal/runtime.Slice" %21) -// CHECK-NEXT: %23 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) -// CHECK-NEXT: %24 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) -// CHECK-NEXT: %25 = getelementptr inbounds %"{{.*}}/runtime/internal/runtime.String", ptr %24, i64 0 -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 5 }, ptr %25, align 8 -// CHECK-NEXT: %26 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %24, 0 -// CHECK-NEXT: %27 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %26, i64 1, 1 -// CHECK-NEXT: %28 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %27, i64 1, 2 -// CHECK-NEXT: %29 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]string,string\]}}).Append"(ptr %23, %"{{.*}}/runtime/internal/runtime.Slice" %28) -// CHECK-NEXT: %30 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) -// CHECK-NEXT: %31 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) -// CHECK-NEXT: %32 = getelementptr inbounds i64, ptr %31, i64 0 -// CHECK-NEXT: store i64 1, ptr %32, align 8 -// CHECK-NEXT: %33 = getelementptr inbounds i64, ptr %31, i64 1 -// CHECK-NEXT: store i64 2, ptr %33, align 8 -// CHECK-NEXT: %34 = getelementptr inbounds i64, ptr %31, i64 2 -// CHECK-NEXT: store i64 3, ptr %34, align 8 -// CHECK-NEXT: %35 = getelementptr inbounds i64, ptr %31, i64 3 -// CHECK-NEXT: store i64 4, ptr %35, align 8 -// CHECK-NEXT: %36 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %31, 0 -// CHECK-NEXT: %37 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %36, i64 4, 1 -// CHECK-NEXT: %38 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %37, i64 4, 2 -// CHECK-NEXT: %39 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %30, %"{{.*}}/runtime/internal/runtime.Slice" %38) -// CHECK-NEXT: %40 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) -// CHECK-NEXT: %41 = getelementptr inbounds i64, ptr %40, i64 0 -// CHECK-NEXT: store i64 1, ptr %41, align 8 -// CHECK-NEXT: %42 = getelementptr inbounds i64, ptr %40, i64 1 -// CHECK-NEXT: store i64 2, ptr %42, align 8 -// CHECK-NEXT: %43 = getelementptr inbounds i64, ptr %40, i64 2 -// CHECK-NEXT: store i64 3, ptr %43, align 8 -// CHECK-NEXT: %44 = getelementptr inbounds i64, ptr %40, i64 3 -// CHECK-NEXT: store i64 4, ptr %44, align 8 -// CHECK-NEXT: %45 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %40, 0 -// CHECK-NEXT: %46 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %45, i64 4, 1 -// CHECK-NEXT: %47 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %46, i64 4, 2 -// CHECK-NEXT: %48 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append2"(ptr %30, %"{{.*}}/runtime/internal/runtime.Slice" %47) -// CHECK-NEXT: %49 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %16, i32 0, i32 0 -// CHECK-NEXT: %50 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %49, align 8 -// CHECK-NEXT: %51 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %16, i32 0, i32 0 -// CHECK-NEXT: %52 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %51, align 8 -// CHECK-NEXT: %53 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %52, 0 -// CHECK-NEXT: %54 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %52, 1 -// CHECK-NEXT: %55 = icmp uge i64 0, %54 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %55, {{.*}}) -// CHECK-NEXT: %56 = getelementptr inbounds i64, ptr %53, i64 0 -// CHECK-NEXT: %57 = load i64, ptr %56, align 8 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %50) +// CHECK-NEXT: %.stack = alloca i8, i64 24, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) +// CHECK-NEXT: %16 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %17 = getelementptr inbounds i64, ptr %16, i64 0 +// CHECK-NEXT: store i64 100, ptr %17, align 8 +// CHECK-NEXT: %18 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %16, 0 +// CHECK-NEXT: %19 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %18, i64 1, 1 +// CHECK-NEXT: %20 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %19, i64 1, 2 +// CHECK-NEXT: %21 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %.stack, %"{{.*}}/runtime/internal/runtime.Slice" %20) +// CHECK-NEXT: %.stack1 = alloca i8, i64 24, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 24, i1 false) +// CHECK-NEXT: %22 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) +// CHECK-NEXT: %23 = getelementptr inbounds %"{{.*}}/runtime/internal/runtime.String", ptr %22, i64 0 +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 5 }, ptr %23, align 8 +// CHECK-NEXT: %24 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %22, 0 +// CHECK-NEXT: %25 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %24, i64 1, 1 +// CHECK-NEXT: %26 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %25, i64 1, 2 +// CHECK-NEXT: %27 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]string,string\]}}).Append"(ptr %.stack1, %"{{.*}}/runtime/internal/runtime.Slice" %26) +// CHECK-NEXT: %.stack2 = alloca i8, i64 24, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack2, i8 0, i64 24, i1 false) +// CHECK-NEXT: %28 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// CHECK-NEXT: %29 = getelementptr inbounds i64, ptr %28, i64 0 +// CHECK-NEXT: store i64 1, ptr %29, align 8 +// CHECK-NEXT: %30 = getelementptr inbounds i64, ptr %28, i64 1 +// CHECK-NEXT: store i64 2, ptr %30, align 8 +// CHECK-NEXT: %31 = getelementptr inbounds i64, ptr %28, i64 2 +// CHECK-NEXT: store i64 3, ptr %31, align 8 +// CHECK-NEXT: %32 = getelementptr inbounds i64, ptr %28, i64 3 +// CHECK-NEXT: store i64 4, ptr %32, align 8 +// CHECK-NEXT: %33 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %28, 0 +// CHECK-NEXT: %34 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %33, i64 4, 1 +// CHECK-NEXT: %35 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %34, i64 4, 2 +// CHECK-NEXT: %36 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %.stack2, %"{{.*}}/runtime/internal/runtime.Slice" %35) +// CHECK-NEXT: %37 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// CHECK-NEXT: %38 = getelementptr inbounds i64, ptr %37, i64 0 +// CHECK-NEXT: store i64 1, ptr %38, align 8 +// CHECK-NEXT: %39 = getelementptr inbounds i64, ptr %37, i64 1 +// CHECK-NEXT: store i64 2, ptr %39, align 8 +// CHECK-NEXT: %40 = getelementptr inbounds i64, ptr %37, i64 2 +// CHECK-NEXT: store i64 3, ptr %40, align 8 +// CHECK-NEXT: %41 = getelementptr inbounds i64, ptr %37, i64 3 +// CHECK-NEXT: store i64 4, ptr %41, align 8 +// CHECK-NEXT: %42 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %37, 0 +// CHECK-NEXT: %43 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %42, i64 4, 1 +// CHECK-NEXT: %44 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %43, i64 4, 2 +// CHECK-NEXT: %45 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append2"(ptr %.stack2, %"{{.*}}/runtime/internal/runtime.Slice" %44) +// CHECK-NEXT: %46 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack, i32 0, i32 0 +// CHECK-NEXT: %47 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %46, align 8 +// CHECK-NEXT: %48 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack, i32 0, i32 0 +// CHECK-NEXT: %49 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %48, align 8 +// CHECK-NEXT: %50 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %49, 0 +// CHECK-NEXT: %51 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %49, 1 +// CHECK-NEXT: %52 = icmp uge i64 0, %51 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %52, {{.*}}) +// CHECK-NEXT: %53 = getelementptr inbounds i64, ptr %50, i64 0 +// CHECK-NEXT: %54 = load i64, ptr %53, align 8 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %47) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %57) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %54) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %58 = getelementptr inbounds %"main.Slice{{\[\[\]string,string\]}}", ptr %23, i32 0, i32 0 -// CHECK-NEXT: %59 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %58, align 8 -// CHECK-NEXT: %60 = getelementptr inbounds %"main.Slice{{\[\[\]string,string\]}}", ptr %23, i32 0, i32 0 -// CHECK-NEXT: %61 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %60, align 8 -// CHECK-NEXT: %62 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %61, 0 -// CHECK-NEXT: %63 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %61, 1 -// CHECK-NEXT: %64 = icmp uge i64 0, %63 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %64, {{.*}}) -// CHECK-NEXT: %65 = getelementptr inbounds %"{{.*}}/runtime/internal/runtime.String", ptr %62, i64 0 -// CHECK-NEXT: %66 = load %"{{.*}}/runtime/internal/runtime.String", ptr %65, align 8 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %59) +// CHECK-NEXT: %55 = getelementptr inbounds %"main.Slice{{\[\[\]string,string\]}}", ptr %.stack1, i32 0, i32 0 +// CHECK-NEXT: %56 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %55, align 8 +// CHECK-NEXT: %57 = getelementptr inbounds %"main.Slice{{\[\[\]string,string\]}}", ptr %.stack1, i32 0, i32 0 +// CHECK-NEXT: %58 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %57, align 8 +// CHECK-NEXT: %59 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %58, 0 +// CHECK-NEXT: %60 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %58, 1 +// CHECK-NEXT: %61 = icmp uge i64 0, %60 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %61, {{.*}}) +// CHECK-NEXT: %62 = getelementptr inbounds %"{{.*}}/runtime/internal/runtime.String", ptr %59, i64 0 +// CHECK-NEXT: %63 = load %"{{.*}}/runtime/internal/runtime.String", ptr %62, align 8 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %56) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %66) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %63) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %67 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %30, i32 0, i32 0 -// CHECK-NEXT: %68 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %67, align 8 -// CHECK-NEXT: %69 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %30, i32 0, i32 0 -// CHECK-NEXT: %70 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %69, align 8 -// CHECK-NEXT: %71 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %70, 0 -// CHECK-NEXT: %72 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %70, 1 -// CHECK-NEXT: %73 = icmp uge i64 0, %72 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %73, {{.*}}) -// CHECK-NEXT: %74 = getelementptr inbounds i64, ptr %71, i64 0 -// CHECK-NEXT: %75 = load i64, ptr %74, align 8 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %68) +// CHECK-NEXT: %64 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack2, i32 0, i32 0 +// CHECK-NEXT: %65 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %64, align 8 +// CHECK-NEXT: %66 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack2, i32 0, i32 0 +// CHECK-NEXT: %67 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %66, align 8 +// CHECK-NEXT: %68 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %67, 0 +// CHECK-NEXT: %69 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %67, 1 +// CHECK-NEXT: %70 = icmp uge i64 0, %69 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %70, {{.*}}) +// CHECK-NEXT: %71 = getelementptr inbounds i64, ptr %68, i64 0 +// CHECK-NEXT: %72 = load i64, ptr %71, align 8 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %65) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %75) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %72) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: ret void // CHECK-NEXT: } diff --git a/cl/_testlibc/atomic/in.go b/cl/_testlibc/atomic/in.go index f1681d9c38..3d272880d8 100644 --- a/cl/_testlibc/atomic/in.go +++ b/cl/_testlibc/atomic/in.go @@ -10,23 +10,23 @@ import ( func main() { var v int64 - // CHECK: store atomic i64 100, ptr %0 seq_cst, align 8 + // CHECK: store atomic i64 100, ptr %.stack seq_cst, align 8 atomic.Store(&v, 100) - // CHECK: %1 = load atomic i64, ptr %0 seq_cst, align 8 + // CHECK: %0 = load atomic i64, ptr %.stack seq_cst, align 8 c.Printf(c.Str("store: %ld\n"), atomic.Load(&v)) - // CHECK: %3 = atomicrmw add ptr %0, i64 1 seq_cst, align 8 + // CHECK: %2 = atomicrmw add ptr %.stack, i64 1 seq_cst, align 8 ret := atomic.Add(&v, 1) c.Printf(c.Str("ret: %ld, v: %ld\n"), ret, v) - // CHECK: %6 = cmpxchg ptr %0, i64 100, i64 102 seq_cst seq_cst, align 8 + // CHECK: %5 = cmpxchg ptr %.stack, i64 100, i64 102 seq_cst seq_cst, align 8 ret, _ = atomic.CompareAndExchange(&v, 100, 102) c.Printf(c.Str("ret: %ld vs 100, v: %ld\n"), ret, v) - // CHECK: %11 = cmpxchg ptr %0, i64 101, i64 102 seq_cst seq_cst, align 8 + // CHECK: %10 = cmpxchg ptr %.stack, i64 101, i64 102 seq_cst seq_cst, align 8 ret, _ = atomic.CompareAndExchange(&v, 101, 102) c.Printf(c.Str("ret: %ld vs 101, v: %ld\n"), ret, v) - // CHECK: %16 = atomicrmw sub ptr %0, i64 1 seq_cst, align 8 + // CHECK: %15 = atomicrmw sub ptr %.stack, i64 1 seq_cst, align 8 ret = atomic.Sub(&v, 1) c.Printf(c.Str("ret: %ld, v: %ld\n"), ret, v) } diff --git a/cl/_testlibgo/atomic/in.go b/cl/_testlibgo/atomic/in.go index d97635e548..e4bb927bde 100644 --- a/cl/_testlibgo/atomic/in.go +++ b/cl/_testlibgo/atomic/in.go @@ -9,32 +9,32 @@ import ( func main() { var v int64 - // CHECK: store atomic i64 100, ptr %0 seq_cst, align 8 - // CHECK: %1 = load atomic i64, ptr %0 seq_cst, align 8 + // CHECK: store atomic i64 100, ptr %.stack seq_cst, align 8 + // CHECK: %0 = load atomic i64, ptr %.stack seq_cst, align 8 atomic.StoreInt64(&v, 100) println("store:", atomic.LoadInt64(&v)) - // CHECK: %2 = atomicrmw add ptr %0, i64 1 seq_cst, align 8 - // CHECK: %3 = add i64 %2, 1 - // CHECK: %4 = load i64, ptr %0, align 8 + // CHECK: %1 = atomicrmw add ptr %.stack, i64 1 seq_cst, align 8 + // CHECK: %2 = add i64 %1, 1 + // CHECK: %3 = load i64, ptr %.stack, align 8 ret := atomic.AddInt64(&v, 1) println("ret:", ret, "v:", v) - // CHECK: %5 = cmpxchg ptr %0, i64 100, i64 102 seq_cst seq_cst, align 8 - // CHECK: %6 = extractvalue { i64, i1 } %5, 1 - // CHECK: %7 = load i64, ptr %0, align 8 + // CHECK: %4 = cmpxchg ptr %.stack, i64 100, i64 102 seq_cst seq_cst, align 8 + // CHECK: %5 = extractvalue { i64, i1 } %4, 1 + // CHECK: %6 = load i64, ptr %.stack, align 8 swp := atomic.CompareAndSwapInt64(&v, 100, 102) println("swp:", swp, "v:", v) - // CHECK: %8 = cmpxchg ptr %0, i64 101, i64 102 seq_cst seq_cst, align 8 - // CHECK: %9 = extractvalue { i64, i1 } %8, 1 - // CHECK: %10 = load i64, ptr %0, align 8 + // CHECK: %7 = cmpxchg ptr %.stack, i64 101, i64 102 seq_cst seq_cst, align 8 + // CHECK: %8 = extractvalue { i64, i1 } %7, 1 + // CHECK: %9 = load i64, ptr %.stack, align 8 swp = atomic.CompareAndSwapInt64(&v, 101, 102) println("swp:", swp, "v:", v) - // CHECK: %11 = atomicrmw add ptr %0, i64 -1 seq_cst, align 8 - // CHECK: %12 = add i64 %11, -1 - // CHECK: %13 = load i64, ptr %0, align 8 + // CHECK: %10 = atomicrmw add ptr %.stack, i64 -1 seq_cst, align 8 + // CHECK: %11 = add i64 %10, -1 + // CHECK: %12 = load i64, ptr %.stack, align 8 ret = atomic.AddInt64(&v, -1) println("ret:", ret, "v:", v) } diff --git a/cl/_testrt/abitype/in.go b/cl/_testrt/abitype/in.go index ced9c0c2d7..39b520e290 100644 --- a/cl/_testrt/abitype/in.go +++ b/cl/_testrt/abitype/in.go @@ -9,42 +9,43 @@ import ( // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 4) -// CHECK-NEXT: store i32 0, ptr %1, align 4 -// CHECK-NEXT: %2 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_int32, ptr undef }, ptr %1, 1 -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %2, ptr %0, align 8 -// CHECK-NEXT: %3 = getelementptr inbounds %main.eface, ptr %0, i32 0, i32 0 -// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 -// CHECK-NEXT: %5 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/abi.(*Type).String"(ptr %4) -// CHECK-NEXT: %6 = call i1 @"{{.*}}/runtime/internal/runtime.StringEqual"(%"{{.*}}/runtime/internal/runtime.String" %5, %"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 5 }) -// CHECK-NEXT: %7 = xor i1 %6, true -// CHECK-NEXT: br i1 %7, label %_llgo_1, label %_llgo_2 +// CHECK-NEXT: %.stack = alloca i8, i64 16, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 4) +// CHECK-NEXT: store i32 0, ptr %0, align 4 +// CHECK-NEXT: %1 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_int32, ptr undef }, ptr %0, 1 +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %1, ptr %.stack, align 8 +// CHECK-NEXT: %2 = getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 +// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 +// CHECK-NEXT: %4 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/abi.(*Type).String"(ptr %3) +// CHECK-NEXT: %5 = call i1 @"{{.*}}/runtime/internal/runtime.StringEqual"(%"{{.*}}/runtime/internal/runtime.String" %4, %"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 5 }) +// CHECK-NEXT: %6 = xor i1 %5, true +// CHECK-NEXT: br i1 %6, label %_llgo_1, label %_llgo_2 // CHECK-EMPTY: // CHECK-NEXT: _llgo_1: ; preds = %_llgo_0 -// CHECK-NEXT: %8 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @1, i64 14 }, ptr %8, align 8 -// CHECK-NEXT: %9 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %8, 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %9) +// CHECK-NEXT: %7 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @1, i64 14 }, ptr %7, align 8 +// CHECK-NEXT: %8 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %7, 1 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %8) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_2: ; preds = %_llgo_0 -// CHECK-NEXT: %10 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 1) -// CHECK-NEXT: store i8 0, ptr %10, align 1 -// CHECK-NEXT: %11 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_uint8, ptr undef }, ptr %10, 1 -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %11, ptr %0, align 8 -// CHECK-NEXT: %12 = getelementptr inbounds %main.eface, ptr %0, i32 0, i32 0 -// CHECK-NEXT: %13 = load ptr, ptr %12, align 8 -// CHECK-NEXT: %14 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/abi.(*Type).String"(ptr %13) -// CHECK-NEXT: %15 = call i1 @"{{.*}}/runtime/internal/runtime.StringEqual"(%"{{.*}}/runtime/internal/runtime.String" %14, %"{{.*}}/runtime/internal/runtime.String" { ptr @3, i64 5 }) -// CHECK-NEXT: %16 = xor i1 %15, true -// CHECK-NEXT: br i1 %16, label %_llgo_3, label %_llgo_4 +// CHECK-NEXT: %9 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 1) +// CHECK-NEXT: store i8 0, ptr %9, align 1 +// CHECK-NEXT: %10 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_uint8, ptr undef }, ptr %9, 1 +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %10, ptr %.stack, align 8 +// CHECK-NEXT: %11 = getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 +// CHECK-NEXT: %12 = load ptr, ptr %11, align 8 +// CHECK-NEXT: %13 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/abi.(*Type).String"(ptr %12) +// CHECK-NEXT: %14 = call i1 @"{{.*}}/runtime/internal/runtime.StringEqual"(%"{{.*}}/runtime/internal/runtime.String" %13, %"{{.*}}/runtime/internal/runtime.String" { ptr @3, i64 5 }) +// CHECK-NEXT: %15 = xor i1 %14, true +// CHECK-NEXT: br i1 %15, label %_llgo_3, label %_llgo_4 // CHECK-EMPTY: // CHECK-NEXT: _llgo_3: ; preds = %_llgo_2 -// CHECK-NEXT: %17 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @4, i64 14 }, ptr %17, align 8 -// CHECK-NEXT: %18 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %17, 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %18) +// CHECK-NEXT: %16 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @4, i64 14 }, ptr %16, align 8 +// CHECK-NEXT: %17 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %16, 1 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %17) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_4: ; preds = %_llgo_2 diff --git a/cl/_testrt/builtin/in.go b/cl/_testrt/builtin/in.go index fdc7ff8da0..9add030e07 100644 --- a/cl/_testrt/builtin/in.go +++ b/cl/_testrt/builtin/in.go @@ -31,10 +31,11 @@ const ( // CHECK-LABEL: define double @main.Float64frombits(i64 %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: store i64 %0, ptr %1, align 8 -// CHECK-NEXT: %2 = load double, ptr %1, align 8 -// CHECK-NEXT: ret double %2 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: store i64 %0, ptr %.stack, align 8 +// CHECK-NEXT: %1 = load double, ptr %.stack, align 8 +// CHECK-NEXT: ret double %1 // CHECK-NEXT: } func Float64frombits(b uint64) float64 { return *(*float64)(unsafe.Pointer(&b)) } diff --git a/cl/_testrt/eface/in.go b/cl/_testrt/eface/in.go index 68b29347b3..a4a246d686 100644 --- a/cl/_testrt/eface/in.go +++ b/cl/_testrt/eface/in.go @@ -19,11 +19,12 @@ func (t *T) Invoke() { // CHECK-LABEL: define void @main.dump(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %1, align 8 -// CHECK-NEXT: %2 = getelementptr inbounds %main.eface, ptr %1, i32 0, i32 0 -// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 -// CHECK-NEXT: call void @main.dumpTyp(ptr %3, %"{{.*}}/runtime/internal/runtime.String" zeroinitializer) +// CHECK-NEXT: %.stack = alloca i8, i64 16, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 +// CHECK-NEXT: %1 = getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: call void @main.dumpTyp(ptr %2, %"{{.*}}/runtime/internal/runtime.String" zeroinitializer) // CHECK-NEXT: ret void // CHECK-NEXT: } func dump(v any) { diff --git a/cl/_testrt/funcaddr/in.go b/cl/_testrt/funcaddr/in.go index 6457b31b93..46d0514e9c 100644 --- a/cl/_testrt/funcaddr/in.go +++ b/cl/_testrt/funcaddr/in.go @@ -21,23 +21,25 @@ func add(a, b int) int { // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: store ptr @main.add, ptr %0, align 8 -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: store ptr @"main.main$1", ptr %1, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %0, align 8 -// CHECK-NEXT: %3 = icmp eq ptr @main.add, %2 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %3) +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: store ptr @main.add, ptr %.stack, align 8 +// CHECK-NEXT: %.stack1 = alloca i8, i64 8, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 8, i1 false) +// CHECK-NEXT: store ptr @"main.main$1", ptr %.stack1, align 8 +// CHECK-NEXT: %0 = load ptr, ptr %.stack, align 8 +// CHECK-NEXT: %1 = icmp eq ptr @main.add, %0 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %1) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %4 = load ptr, ptr %0, align 8 -// CHECK-NEXT: %5 = load ptr, ptr %0, align 8 -// CHECK-NEXT: %6 = icmp eq ptr %4, %5 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %6) +// CHECK-NEXT: %2 = load ptr, ptr %.stack, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %.stack, align 8 +// CHECK-NEXT: %4 = icmp eq ptr %2, %3 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %4) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %7 = load ptr, ptr %1, align 8 -// CHECK-NEXT: %8 = load ptr, ptr %1, align 8 -// CHECK-NEXT: %9 = icmp eq ptr %7, %8 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %9) +// CHECK-NEXT: %5 = load ptr, ptr %.stack1, align 8 +// CHECK-NEXT: %6 = load ptr, ptr %.stack1, align 8 +// CHECK-NEXT: %7 = icmp eq ptr %5, %6 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %7) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: ret void // CHECK-NEXT: } diff --git a/cl/_testrt/funcdecl/in.go b/cl/_testrt/funcdecl/in.go index 8efd3f4635..8fe57dda4d 100644 --- a/cl/_testrt/funcdecl/in.go +++ b/cl/_testrt/funcdecl/in.go @@ -73,13 +73,14 @@ func check(fn func()) { // CHECK-LABEL: define ptr @main.closurePtr(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %1, align 8 -// CHECK-NEXT: %2 = getelementptr inbounds %main.rtype, ptr %1, i32 0, i32 1 -// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 -// CHECK-NEXT: %4 = getelementptr inbounds { ptr, ptr }, ptr %3, i32 0, i32 0 -// CHECK-NEXT: %5 = load ptr, ptr %4, align 8 -// CHECK-NEXT: ret ptr %5 +// CHECK-NEXT: %.stack = alloca i8, i64 16, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 +// CHECK-NEXT: %1 = getelementptr inbounds %main.rtype, ptr %.stack, i32 0, i32 1 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %3 = getelementptr inbounds { ptr, ptr }, ptr %2, i32 0, i32 0 +// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 +// CHECK-NEXT: ret ptr %4 // CHECK-NEXT: } func closurePtr(a any) unsafe.Pointer { diff --git a/cl/_testrt/index/in.go b/cl/_testrt/index/in.go index 8f19ac4768..0aea23a80c 100644 --- a/cl/_testrt/index/in.go +++ b/cl/_testrt/index/in.go @@ -119,34 +119,35 @@ type S []int // CHECK-NEXT: %51 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/internal/runtime.StringFromUint64"(i64 %50) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %51) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %52 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) -// CHECK-NEXT: %53 = getelementptr inbounds i64, ptr %52, i64 0 -// CHECK-NEXT: %54 = getelementptr inbounds i64, ptr %52, i64 1 -// CHECK-NEXT: store i64 1, ptr %53, align 8 -// CHECK-NEXT: store i64 2, ptr %54, align 8 -// CHECK-NEXT: %55 = getelementptr inbounds i64, ptr %52, i64 1 -// CHECK-NEXT: %56 = load i64, ptr %55, align 8 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %56) +// CHECK-NEXT: %.stack = alloca i8, i64 16, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// CHECK-NEXT: %52 = getelementptr inbounds i64, ptr %.stack, i64 0 +// CHECK-NEXT: %53 = getelementptr inbounds i64, ptr %.stack, i64 1 +// CHECK-NEXT: store i64 1, ptr %52, align 8 +// CHECK-NEXT: store i64 2, ptr %53, align 8 +// CHECK-NEXT: %54 = getelementptr inbounds i64, ptr %.stack, i64 1 +// CHECK-NEXT: %55 = load i64, ptr %54, align 8 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %55) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %57 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) -// CHECK-NEXT: %58 = getelementptr inbounds i64, ptr %57, i64 0 -// CHECK-NEXT: store i64 1, ptr %58, align 8 -// CHECK-NEXT: %59 = getelementptr inbounds i64, ptr %57, i64 1 -// CHECK-NEXT: store i64 2, ptr %59, align 8 -// CHECK-NEXT: %60 = getelementptr inbounds i64, ptr %57, i64 2 -// CHECK-NEXT: store i64 3, ptr %60, align 8 -// CHECK-NEXT: %61 = getelementptr inbounds i64, ptr %57, i64 3 -// CHECK-NEXT: store i64 4, ptr %61, align 8 -// CHECK-NEXT: %62 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %57, 0 -// CHECK-NEXT: %63 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %62, i64 4, 1 -// CHECK-NEXT: %64 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %63, i64 4, 2 -// CHECK-NEXT: %65 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %64, 0 -// CHECK-NEXT: %66 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %64, 1 -// CHECK-NEXT: %67 = icmp uge i64 1, %66 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %67, i64 1, i1 true, i64 %66) -// CHECK-NEXT: %68 = getelementptr inbounds i64, ptr %65, i64 1 -// CHECK-NEXT: %69 = load i64, ptr %68, align 8 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %69) +// CHECK-NEXT: %56 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// CHECK-NEXT: %57 = getelementptr inbounds i64, ptr %56, i64 0 +// CHECK-NEXT: store i64 1, ptr %57, align 8 +// CHECK-NEXT: %58 = getelementptr inbounds i64, ptr %56, i64 1 +// CHECK-NEXT: store i64 2, ptr %58, align 8 +// CHECK-NEXT: %59 = getelementptr inbounds i64, ptr %56, i64 2 +// CHECK-NEXT: store i64 3, ptr %59, align 8 +// CHECK-NEXT: %60 = getelementptr inbounds i64, ptr %56, i64 3 +// CHECK-NEXT: store i64 4, ptr %60, align 8 +// CHECK-NEXT: %61 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %56, 0 +// CHECK-NEXT: %62 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %61, i64 4, 1 +// CHECK-NEXT: %63 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %62, i64 4, 2 +// CHECK-NEXT: %64 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %63, 0 +// CHECK-NEXT: %65 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %63, 1 +// CHECK-NEXT: %66 = icmp uge i64 1, %65 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %66, i64 1, i1 true, i64 %65) +// CHECK-NEXT: %67 = getelementptr inbounds i64, ptr %64, i64 1 +// CHECK-NEXT: %68 = load i64, ptr %67, align 8 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %68) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 0) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) diff --git a/cl/_testrt/len/in.go b/cl/_testrt/len/in.go index 3bd00461b1..5d1ecae09c 100644 --- a/cl/_testrt/len/in.go +++ b/cl/_testrt/len/in.go @@ -10,91 +10,93 @@ type data struct { // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 56) -// CHECK-NEXT: %1 = getelementptr inbounds %main.data, ptr %0, i32 0, i32 0 -// CHECK-NEXT: %2 = load %"{{.*}}/runtime/internal/runtime.String", ptr %1, align 8 -// CHECK-NEXT: %3 = extractvalue %"{{.*}}/runtime/internal/runtime.String" %2, 1 -// CHECK-NEXT: %4 = getelementptr inbounds %main.data, ptr %0, i32 0, i32 1 -// CHECK-NEXT: %5 = load ptr, ptr %4, align 8 -// CHECK-NEXT: %6 = call i64 @"{{.*}}/runtime/internal/runtime.ChanLen"(ptr %5) -// CHECK-NEXT: %7 = getelementptr inbounds %main.data, ptr %0, i32 0, i32 2 -// CHECK-NEXT: %8 = load ptr, ptr %7, align 8 -// CHECK-NEXT: %9 = call i64 @"{{.*}}/runtime/internal/runtime.MapLen"(ptr %8) -// CHECK-NEXT: %10 = getelementptr inbounds %main.data, ptr %0, i32 0, i32 3 -// CHECK-NEXT: %11 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %10, align 8 -// CHECK-NEXT: %12 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %11, 1 -// CHECK-NEXT: %13 = getelementptr inbounds %main.data, ptr %0, i32 0, i32 1 -// CHECK-NEXT: %14 = load ptr, ptr %13, align 8 -// CHECK-NEXT: %15 = call i64 @"{{.*}}/runtime/internal/runtime.ChanCap"(ptr %14) -// CHECK-NEXT: %16 = getelementptr inbounds %main.data, ptr %0, i32 0, i32 3 -// CHECK-NEXT: %17 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %16, align 8 -// CHECK-NEXT: %18 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %17, 2 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %3) +// CHECK-NEXT: %.stack = alloca i8, i64 56, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 56, i1 false) +// CHECK-NEXT: %0 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 0 +// CHECK-NEXT: %1 = load %"{{.*}}/runtime/internal/runtime.String", ptr %0, align 8 +// CHECK-NEXT: %2 = extractvalue %"{{.*}}/runtime/internal/runtime.String" %1, 1 +// CHECK-NEXT: %3 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 1 +// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 +// CHECK-NEXT: %5 = call i64 @"{{.*}}/runtime/internal/runtime.ChanLen"(ptr %4) +// CHECK-NEXT: %6 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 2 +// CHECK-NEXT: %7 = load ptr, ptr %6, align 8 +// CHECK-NEXT: %8 = call i64 @"{{.*}}/runtime/internal/runtime.MapLen"(ptr %7) +// CHECK-NEXT: %9 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 3 +// CHECK-NEXT: %10 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %9, align 8 +// CHECK-NEXT: %11 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %10, 1 +// CHECK-NEXT: %12 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 1 +// CHECK-NEXT: %13 = load ptr, ptr %12, align 8 +// CHECK-NEXT: %14 = call i64 @"{{.*}}/runtime/internal/runtime.ChanCap"(ptr %13) +// CHECK-NEXT: %15 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 3 +// CHECK-NEXT: %16 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %15, align 8 +// CHECK-NEXT: %17 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %16, 2 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %2) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %6) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %5) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %9) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %8) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %12) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %11) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %15) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %14) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %18) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %17) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %19 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 56) -// CHECK-NEXT: %20 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 0 -// CHECK-NEXT: %21 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 1 -// CHECK-NEXT: %22 = call ptr @"{{.*}}/runtime/internal/runtime.NewChan"(i64 8, i64 2) -// CHECK-NEXT: %23 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 2 -// CHECK-NEXT: %24 = call ptr @"{{.*}}/runtime/internal/runtime.MakeMap"(ptr @"map[_llgo_int]_llgo_string", i64 1) -// CHECK-NEXT: %25 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 8) -// CHECK-NEXT: store i64 1, ptr %25, align 8 -// CHECK-NEXT: %26 = call ptr @"{{.*}}/runtime/internal/runtime.MapAssign"(ptr @"map[_llgo_int]_llgo_string", ptr %24, ptr %25) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @16, i64 5 }, ptr %26, align 8 -// CHECK-NEXT: %27 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 3 -// CHECK-NEXT: %28 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) -// CHECK-NEXT: %29 = getelementptr inbounds i64, ptr %28, i64 0 -// CHECK-NEXT: store i64 1, ptr %29, align 8 -// CHECK-NEXT: %30 = getelementptr inbounds i64, ptr %28, i64 1 -// CHECK-NEXT: store i64 2, ptr %30, align 8 -// CHECK-NEXT: %31 = getelementptr inbounds i64, ptr %28, i64 2 -// CHECK-NEXT: store i64 3, ptr %31, align 8 -// CHECK-NEXT: %32 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %28, 0 -// CHECK-NEXT: %33 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %32, i64 3, 1 -// CHECK-NEXT: %34 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %33, i64 3, 2 -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @16, i64 5 }, ptr %20, align 8 +// CHECK-NEXT: %.stack1 = alloca i8, i64 56, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 56, i1 false) +// CHECK-NEXT: %18 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 0 +// CHECK-NEXT: %19 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 1 +// CHECK-NEXT: %20 = call ptr @"{{.*}}/runtime/internal/runtime.NewChan"(i64 8, i64 2) +// CHECK-NEXT: %21 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 2 +// CHECK-NEXT: %22 = call ptr @"{{.*}}/runtime/internal/runtime.MakeMap"(ptr @"map[_llgo_int]_llgo_string", i64 1) +// CHECK-NEXT: %23 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 8) +// CHECK-NEXT: store i64 1, ptr %23, align 8 +// CHECK-NEXT: %24 = call ptr @"{{.*}}/runtime/internal/runtime.MapAssign"(ptr @"map[_llgo_int]_llgo_string", ptr %22, ptr %23) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @16, i64 5 }, ptr %24, align 8 +// CHECK-NEXT: %25 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 3 +// CHECK-NEXT: %26 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// CHECK-NEXT: %27 = getelementptr inbounds i64, ptr %26, i64 0 +// CHECK-NEXT: store i64 1, ptr %27, align 8 +// CHECK-NEXT: %28 = getelementptr inbounds i64, ptr %26, i64 1 +// CHECK-NEXT: store i64 2, ptr %28, align 8 +// CHECK-NEXT: %29 = getelementptr inbounds i64, ptr %26, i64 2 +// CHECK-NEXT: store i64 3, ptr %29, align 8 +// CHECK-NEXT: %30 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %26, 0 +// CHECK-NEXT: %31 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %30, i64 3, 1 +// CHECK-NEXT: %32 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %31, i64 3, 2 +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @16, i64 5 }, ptr %18, align 8 +// CHECK-NEXT: store ptr %20, ptr %19, align 8 // CHECK-NEXT: store ptr %22, ptr %21, align 8 -// CHECK-NEXT: store ptr %24, ptr %23, align 8 -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.Slice" %34, ptr %27, align 8 -// CHECK-NEXT: %35 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 0 -// CHECK-NEXT: %36 = load %"{{.*}}/runtime/internal/runtime.String", ptr %35, align 8 -// CHECK-NEXT: %37 = extractvalue %"{{.*}}/runtime/internal/runtime.String" %36, 1 -// CHECK-NEXT: %38 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 1 -// CHECK-NEXT: %39 = load ptr, ptr %38, align 8 -// CHECK-NEXT: %40 = call i64 @"{{.*}}/runtime/internal/runtime.ChanLen"(ptr %39) -// CHECK-NEXT: %41 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 2 -// CHECK-NEXT: %42 = load ptr, ptr %41, align 8 -// CHECK-NEXT: %43 = call i64 @"{{.*}}/runtime/internal/runtime.MapLen"(ptr %42) -// CHECK-NEXT: %44 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 3 -// CHECK-NEXT: %45 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %44, align 8 -// CHECK-NEXT: %46 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %45, 1 -// CHECK-NEXT: %47 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 1 -// CHECK-NEXT: %48 = load ptr, ptr %47, align 8 -// CHECK-NEXT: %49 = call i64 @"{{.*}}/runtime/internal/runtime.ChanCap"(ptr %48) -// CHECK-NEXT: %50 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 3 -// CHECK-NEXT: %51 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %50, align 8 -// CHECK-NEXT: %52 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %51, 2 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %37) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.Slice" %32, ptr %25, align 8 +// CHECK-NEXT: %33 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 0 +// CHECK-NEXT: %34 = load %"{{.*}}/runtime/internal/runtime.String", ptr %33, align 8 +// CHECK-NEXT: %35 = extractvalue %"{{.*}}/runtime/internal/runtime.String" %34, 1 +// CHECK-NEXT: %36 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 1 +// CHECK-NEXT: %37 = load ptr, ptr %36, align 8 +// CHECK-NEXT: %38 = call i64 @"{{.*}}/runtime/internal/runtime.ChanLen"(ptr %37) +// CHECK-NEXT: %39 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 2 +// CHECK-NEXT: %40 = load ptr, ptr %39, align 8 +// CHECK-NEXT: %41 = call i64 @"{{.*}}/runtime/internal/runtime.MapLen"(ptr %40) +// CHECK-NEXT: %42 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 3 +// CHECK-NEXT: %43 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %42, align 8 +// CHECK-NEXT: %44 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %43, 1 +// CHECK-NEXT: %45 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 1 +// CHECK-NEXT: %46 = load ptr, ptr %45, align 8 +// CHECK-NEXT: %47 = call i64 @"{{.*}}/runtime/internal/runtime.ChanCap"(ptr %46) +// CHECK-NEXT: %48 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 3 +// CHECK-NEXT: %49 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %48, align 8 +// CHECK-NEXT: %50 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %49, 2 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %35) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %40) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %38) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %43) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %41) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %46) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %44) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %49) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %47) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %52) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %50) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: ret void // CHECK-NEXT: } diff --git a/cl/_testrt/tpunsafe/in.go b/cl/_testrt/tpunsafe/in.go index a19bd33d6e..ae7cf159cd 100644 --- a/cl/_testrt/tpunsafe/in.go +++ b/cl/_testrt/tpunsafe/in.go @@ -18,10 +18,12 @@ type M[T any] struct { // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 12) -// CHECK-NEXT: call void @"main.(*M[bool]).check"(ptr %0, i64 1, i64 8, i64 1) -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) -// CHECK-NEXT: call void @"main.(*M[int64]).check"(ptr %1, i64 8, i64 16, i64 8) +// CHECK-NEXT: %.stack = alloca i8, i64 12, align 1 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 12, i1 false) +// CHECK-NEXT: call void @"main.(*M[bool]).check"(ptr %.stack, i64 1, i64 8, i64 1) +// CHECK-NEXT: %.stack1 = alloca i8, i64 32, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 32, i1 false) +// CHECK-NEXT: call void @"main.(*M[int64]).check"(ptr %.stack1, i64 8, i64 16, i64 8) // CHECK-NEXT: ret void // CHECK-NEXT: } func main() { diff --git a/cl/_testrt/typalias/in.go b/cl/_testrt/typalias/in.go index f35416ed36..70ceebc123 100644 --- a/cl/_testrt/typalias/in.go +++ b/cl/_testrt/typalias/in.go @@ -38,12 +38,13 @@ func Print(p *Foo) { // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %1 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 0 -// CHECK-NEXT: %2 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 1 -// CHECK-NEXT: store i32 100, ptr %1, align 4 -// CHECK-NEXT: store i1 true, ptr %2, align 1 -// CHECK-NEXT: call void @main.Print(ptr %0) +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 4 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: %0 = getelementptr inbounds { i32, i1 }, ptr %.stack, i32 0, i32 0 +// CHECK-NEXT: %1 = getelementptr inbounds { i32, i1 }, ptr %.stack, i32 0, i32 1 +// CHECK-NEXT: store i32 100, ptr %0, align 4 +// CHECK-NEXT: store i1 true, ptr %1, align 1 +// CHECK-NEXT: call void @main.Print(ptr %.stack) // CHECK-NEXT: ret void // CHECK-NEXT: } func main() { diff --git a/cl/_testrt/vamethod/in.go b/cl/_testrt/vamethod/in.go index a7ef707b26..21ddc17cf4 100644 --- a/cl/_testrt/vamethod/in.go +++ b/cl/_testrt/vamethod/in.go @@ -56,66 +56,67 @@ type IFmt interface { // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: call void @"main.(*CFmt).SetFormat"(ptr %0, ptr @0) -// CHECK-NEXT: %1 = getelementptr inbounds %main.CFmt, ptr %0, i32 0, i32 0 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: %3 = call i32 (ptr, ...) @printf(ptr %2, ptr @1, i64 100) -// CHECK-NEXT: call void @"main.(*CFmt).SetFormat"(ptr %0, ptr @2) -// CHECK-NEXT: %4 = getelementptr inbounds %main.CFmt, ptr %0, i32 0, i32 0 -// CHECK-NEXT: %5 = load ptr, ptr %4, align 8 -// CHECK-NEXT: %6 = call i32 (ptr, ...) @printf(ptr %5, i64 200, ptr @3) -// CHECK-NEXT: %7 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %8 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @"*_llgo_main.CFmt", ptr undef }, ptr %7, 1 -// CHECK-NEXT: %9 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %8, 0 -// CHECK-NEXT: %10 = call i1 @"{{.*}}/runtime/internal/runtime.Implements"(ptr @_llgo_main.IFmt, ptr %9) -// CHECK-NEXT: br i1 %10, label %_llgo_3, label %_llgo_4 +// CHECK-NEXT: %.stack = alloca i8, i64 8, align 8 +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// CHECK-NEXT: call void @"main.(*CFmt).SetFormat"(ptr %.stack, ptr @0) +// CHECK-NEXT: %0 = getelementptr inbounds %main.CFmt, ptr %.stack, i32 0, i32 0 +// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 +// CHECK-NEXT: %2 = call i32 (ptr, ...) @printf(ptr %1, ptr @1, i64 100) +// CHECK-NEXT: call void @"main.(*CFmt).SetFormat"(ptr %.stack, ptr @2) +// CHECK-NEXT: %3 = getelementptr inbounds %main.CFmt, ptr %.stack, i32 0, i32 0 +// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 +// CHECK-NEXT: %5 = call i32 (ptr, ...) @printf(ptr %4, i64 200, ptr @3) +// CHECK-NEXT: %6 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %7 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @"*_llgo_main.CFmt", ptr undef }, ptr %6, 1 +// CHECK-NEXT: %8 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %7, 0 +// CHECK-NEXT: %9 = call i1 @"{{.*}}/runtime/internal/runtime.Implements"(ptr @_llgo_main.IFmt, ptr %8) +// CHECK-NEXT: br i1 %9, label %_llgo_3, label %_llgo_4 // CHECK-EMPTY: // CHECK-NEXT: _llgo_1: ; preds = %_llgo_5 -// CHECK-NEXT: %11 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @20, i64 5 }, ptr %11, align 8 -// CHECK-NEXT: %12 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %11, 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %12) +// CHECK-NEXT: %10 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @20, i64 5 }, ptr %10, align 8 +// CHECK-NEXT: %11 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %10, 1 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %11) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_2: ; preds = %_llgo_5 -// CHECK-NEXT: %13 = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %37) -// CHECK-NEXT: %14 = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %37, 0 -// CHECK-NEXT: %15 = getelementptr ptr, ptr %14, i64 4 -// CHECK-NEXT: %16 = load ptr, ptr %15, align 8 -// CHECK-NEXT: %17 = insertvalue { ptr, ptr } undef, ptr %16, 0 -// CHECK-NEXT: %18 = insertvalue { ptr, ptr } %17, ptr %13, 1 -// CHECK-NEXT: %19 = extractvalue { ptr, ptr } %18, 1 -// CHECK-NEXT: %20 = extractvalue { ptr, ptr } %18, 0 -// CHECK-NEXT: call void %20(ptr %19, ptr @18) -// CHECK-NEXT: %21 = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %37) -// CHECK-NEXT: %22 = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %37, 0 -// CHECK-NEXT: %23 = getelementptr ptr, ptr %22, i64 3 -// CHECK-NEXT: %24 = load ptr, ptr %23, align 8 -// CHECK-NEXT: %25 = insertvalue { ptr, ptr } undef, ptr %24, 0 -// CHECK-NEXT: %26 = insertvalue { ptr, ptr } %25, ptr %21, 1 -// CHECK-NEXT: %27 = extractvalue { ptr, ptr } %26, 1 -// CHECK-NEXT: %28 = extractvalue { ptr, ptr } %26, 0 -// CHECK-NEXT: %29 = call i32 (ptr, ...) %28(ptr %27, ptr @19, i64 100, i64 200) +// CHECK-NEXT: %12 = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %36) +// CHECK-NEXT: %13 = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %36, 0 +// CHECK-NEXT: %14 = getelementptr ptr, ptr %13, i64 4 +// CHECK-NEXT: %15 = load ptr, ptr %14, align 8 +// CHECK-NEXT: %16 = insertvalue { ptr, ptr } undef, ptr %15, 0 +// CHECK-NEXT: %17 = insertvalue { ptr, ptr } %16, ptr %12, 1 +// CHECK-NEXT: %18 = extractvalue { ptr, ptr } %17, 1 +// CHECK-NEXT: %19 = extractvalue { ptr, ptr } %17, 0 +// CHECK-NEXT: call void %19(ptr %18, ptr @18) +// CHECK-NEXT: %20 = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %36) +// CHECK-NEXT: %21 = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %36, 0 +// CHECK-NEXT: %22 = getelementptr ptr, ptr %21, i64 3 +// CHECK-NEXT: %23 = load ptr, ptr %22, align 8 +// CHECK-NEXT: %24 = insertvalue { ptr, ptr } undef, ptr %23, 0 +// CHECK-NEXT: %25 = insertvalue { ptr, ptr } %24, ptr %20, 1 +// CHECK-NEXT: %26 = extractvalue { ptr, ptr } %25, 1 +// CHECK-NEXT: %27 = extractvalue { ptr, ptr } %25, 0 +// CHECK-NEXT: %28 = call i32 (ptr, ...) %27(ptr %26, ptr @19, i64 100, i64 200) // CHECK-NEXT: ret void // CHECK-EMPTY: // CHECK-NEXT: _llgo_3: ; preds = %_llgo_0 -// CHECK-NEXT: %30 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %8, 1 -// CHECK-NEXT: %31 = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface$a85zs5wWQQoPIERm_en8plssh4spdIeeXZPC-E0TDh0", ptr %9) -// CHECK-NEXT: %32 = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr %31, 0 -// CHECK-NEXT: %33 = insertvalue %"{{.*}}/runtime/internal/runtime.iface" %32, ptr %30, 1 -// CHECK-NEXT: %34 = insertvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } undef, %"{{.*}}/runtime/internal/runtime.iface" %33, 0 -// CHECK-NEXT: %35 = insertvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %34, i1 true, 1 +// CHECK-NEXT: %29 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %7, 1 +// CHECK-NEXT: %30 = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface$a85zs5wWQQoPIERm_en8plssh4spdIeeXZPC-E0TDh0", ptr %8) +// CHECK-NEXT: %31 = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr %30, 0 +// CHECK-NEXT: %32 = insertvalue %"{{.*}}/runtime/internal/runtime.iface" %31, ptr %29, 1 +// CHECK-NEXT: %33 = insertvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } undef, %"{{.*}}/runtime/internal/runtime.iface" %32, 0 +// CHECK-NEXT: %34 = insertvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %33, i1 true, 1 // CHECK-NEXT: br label %_llgo_5 // CHECK-EMPTY: // CHECK-NEXT: _llgo_4: ; preds = %_llgo_0 // CHECK-NEXT: br label %_llgo_5 // CHECK-EMPTY: // CHECK-NEXT: _llgo_5: ; preds = %_llgo_4, %_llgo_3 -// CHECK-NEXT: %36 = phi { %"{{.*}}/runtime/internal/runtime.iface", i1 } [ %35, %_llgo_3 ], [ zeroinitializer, %_llgo_4 ] -// CHECK-NEXT: %37 = extractvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %36, 0 -// CHECK-NEXT: %38 = extractvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %36, 1 -// CHECK-NEXT: br i1 %38, label %_llgo_2, label %_llgo_1 +// CHECK-NEXT: %35 = phi { %"{{.*}}/runtime/internal/runtime.iface", i1 } [ %34, %_llgo_3 ], [ zeroinitializer, %_llgo_4 ] +// CHECK-NEXT: %36 = extractvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %35, 0 +// CHECK-NEXT: %37 = extractvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %35, 1 +// CHECK-NEXT: br i1 %37, label %_llgo_2, label %_llgo_1 // CHECK-NEXT: } func main() { cfmt := &CFmt{} From ed63bc4a090eca3b13683e10bad9268c512a69a8 Mon Sep 17 00:00:00 2001 From: Rick Guo Date: Sat, 1 Aug 2026 16:15:49 +0800 Subject: [PATCH 07/12] fix: keep golden IR before escape transform --- cl/_testdata/print/in.go | 33 +++--- cl/_testgo/alias/in.go | 10 +- cl/_testgo/cgobasic/cgobasic.go | 66 ++++++------ cl/_testgo/cgocfiles/cgocfiles.go | 11 +- cl/_testgo/cgodefer/cgodefer.go | 11 +- cl/_testgo/cgomacro/cgomacro.go | 58 +++++------ cl/_testgo/cgopython/cgopython.go | 11 +- cl/_testgo/cursor/in.go | 9 +- cl/_testgo/returnorder/in.go | 17 ++-- cl/_testgo/tprecurfn/in.go | 27 +++-- cl/_testgo/tptypes/in.go | 161 +++++++++++++++--------------- cl/_testlibc/atomic/in.go | 12 +-- cl/_testlibgo/atomic/in.go | 28 +++--- cl/_testrt/abitype/in.go | 59 ++++++----- cl/_testrt/builtin/in.go | 9 +- cl/_testrt/eface/in.go | 11 +- cl/_testrt/funcaddr/in.go | 32 +++--- cl/_testrt/funcdecl/in.go | 15 ++- cl/_testrt/index/in.go | 55 +++++----- cl/_testrt/len/in.go | 148 ++++++++++++++------------- cl/_testrt/tpunsafe/in.go | 10 +- cl/_testrt/typalias/in.go | 13 ++- cl/_testrt/vamethod/in.go | 93 +++++++++-------- internal/build/build.go | 8 +- internal/llgen/llgenf.go | 13 +-- internal/llgen/llgenf_test.go | 7 ++ 26 files changed, 452 insertions(+), 475 deletions(-) diff --git a/cl/_testdata/print/in.go b/cl/_testdata/print/in.go index ad97ff65d7..b934f989bc 100644 --- a/cl/_testdata/print/in.go +++ b/cl/_testdata/print/in.go @@ -40,23 +40,22 @@ type stringStruct struct { // CHECK-NEXT: _llgo_0: // CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) // CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" %0, ptr %1, align 8 -// CHECK-NEXT: %.stack = alloca i8, i64 24, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) -// CHECK-NEXT: %2 = call ptr @main.stringStructOf(ptr %1) -// CHECK-NEXT: %3 = getelementptr inbounds %main.stringStruct, ptr %2, i32 0, i32 0 -// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 -// CHECK-NEXT: %5 = getelementptr inbounds %main.slice, ptr %.stack, i32 0, i32 0 -// CHECK-NEXT: store ptr %4, ptr %5, align 8 -// CHECK-NEXT: %6 = getelementptr inbounds %main.stringStruct, ptr %2, i32 0, i32 1 -// CHECK-NEXT: %7 = load i64, ptr %6, align 8 -// CHECK-NEXT: %8 = getelementptr inbounds %main.slice, ptr %.stack, i32 0, i32 1 -// CHECK-NEXT: store i64 %7, ptr %8, align 8 -// CHECK-NEXT: %9 = getelementptr inbounds %main.stringStruct, ptr %2, i32 0, i32 1 -// CHECK-NEXT: %10 = load i64, ptr %9, align 8 -// CHECK-NEXT: %11 = getelementptr inbounds %main.slice, ptr %.stack, i32 0, i32 2 -// CHECK-NEXT: store i64 %10, ptr %11, align 8 -// CHECK-NEXT: %12 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %.stack, align 8 -// CHECK-NEXT: ret %"{{.*}}/runtime/internal/runtime.Slice" %12 +// CHECK-NEXT: %2 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// CHECK-NEXT: %3 = call ptr @main.stringStructOf(ptr %1) +// CHECK-NEXT: %4 = getelementptr inbounds %main.stringStruct, ptr %3, i32 0, i32 0 +// CHECK-NEXT: %5 = load ptr, ptr %4, align 8 +// CHECK-NEXT: %6 = getelementptr inbounds %main.slice, ptr %2, i32 0, i32 0 +// CHECK-NEXT: store ptr %5, ptr %6, align 8 +// CHECK-NEXT: %7 = getelementptr inbounds %main.stringStruct, ptr %3, i32 0, i32 1 +// CHECK-NEXT: %8 = load i64, ptr %7, align 8 +// CHECK-NEXT: %9 = getelementptr inbounds %main.slice, ptr %2, i32 0, i32 1 +// CHECK-NEXT: store i64 %8, ptr %9, align 8 +// CHECK-NEXT: %10 = getelementptr inbounds %main.stringStruct, ptr %3, i32 0, i32 1 +// CHECK-NEXT: %11 = load i64, ptr %10, align 8 +// CHECK-NEXT: %12 = getelementptr inbounds %main.slice, ptr %2, i32 0, i32 2 +// CHECK-NEXT: store i64 %11, ptr %12, align 8 +// CHECK-NEXT: %13 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %2, align 8 +// CHECK-NEXT: ret %"{{.*}}/runtime/internal/runtime.Slice" %13 // CHECK-NEXT: } func bytes(s string) (ret []byte) { diff --git a/cl/_testgo/alias/in.go b/cl/_testgo/alias/in.go index 9c24794a0e..20d91abba6 100644 --- a/cl/_testgo/alias/in.go +++ b/cl/_testgo/alias/in.go @@ -46,12 +46,12 @@ func (p *Point) Scale(factor float64) { // CHECK-LABEL: define void @main.main(){{.*}} { func main() { - // CHECK: %.stack = alloca i8, i64 16, align 8 - // CHECK: call void @"main.(*Point).Scale"(ptr %.stack, double 2.000000e+00) - // CHECK: call void @"main.(*Point).Move"(ptr %.stack, double 3.000000e+00, double 4.000000e+00) - // CHECK: call void @"{{.*}}PrintFloat"(double %3) + // CHECK: call ptr @"{{.*}}AllocZ"(i64 16) + // CHECK: call void @"main.(*Point).Scale"(ptr %0, double 2.000000e+00) + // CHECK: call void @"main.(*Point).Move"(ptr %0, double 3.000000e+00, double 4.000000e+00) + // CHECK: call void @"{{.*}}PrintFloat"(double %4) // CHECK-NEXT: call void @"{{.*}}PrintByte"(i8 32) - // CHECK-NEXT: call void @"{{.*}}PrintFloat"(double %5) + // CHECK-NEXT: call void @"{{.*}}PrintFloat"(double %6) // CHECK-NEXT: call void @"{{.*}}PrintByte"(i8 10) // CHECK-NEXT: ret void pt := &MyPoint{1, 2} diff --git a/cl/_testgo/cgobasic/cgobasic.go b/cl/_testgo/cgobasic/cgobasic.go index cb214c23ba..8017d92376 100644 --- a/cl/_testgo/cgobasic/cgobasic.go +++ b/cl/_testgo/cgobasic/cgobasic.go @@ -14,62 +14,56 @@ import ( // CHECK-LABEL: define double @main._Cfunc_cos(double %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_cos, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: %3 = call double %2(double %0) -// CHECK-NEXT: ret double %3 +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_cos, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 +// CHECK-NEXT: %4 = call double %3(double %0) +// CHECK-NEXT: ret double %4 // CHECK-NEXT: } // CHECK-LABEL: define [0 x i8] @main._Cfunc_free(ptr %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: %3 = call [0 x i8] %2(ptr %0) -// CHECK-NEXT: ret [0 x i8] %3 +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 +// CHECK-NEXT: %4 = call [0 x i8] %3(ptr %0) +// CHECK-NEXT: ret [0 x i8] %4 // CHECK-NEXT: } // CHECK-LABEL: define double @main._Cfunc_log(double %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_log, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: %3 = call double %2(double %0) -// CHECK-NEXT: ret double %3 +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_log, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 +// CHECK-NEXT: %4 = call double %3(double %0) +// CHECK-NEXT: ret double %4 // CHECK-NEXT: } // CHECK-LABEL: define i32 @main._Cfunc_puts(ptr %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_puts, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: %3 = call i32 %2(ptr %0) -// CHECK-NEXT: ret i32 %3 +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_puts, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 +// CHECK-NEXT: %4 = call i32 %3(ptr %0) +// CHECK-NEXT: ret i32 %4 // CHECK-NEXT: } // CHECK-LABEL: define double @main._Cfunc_sin(double %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sin, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: %3 = call double %2(double %0) -// CHECK-NEXT: ret double %3 +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sin, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 +// CHECK-NEXT: %4 = call double %3(double %0) +// CHECK-NEXT: ret double %4 // CHECK-NEXT: } // CHECK-LABEL: define double @main._Cfunc_sqrt(double %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sqrt, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: %3 = call double %2(double %0) -// CHECK-NEXT: ret double %3 +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sqrt, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 +// CHECK-NEXT: %4 = call double %3(double %0) +// CHECK-NEXT: ret double %4 // CHECK-NEXT: } // CHECK-LABEL: define void @main.init(){{.*}} { diff --git a/cl/_testgo/cgocfiles/cgocfiles.go b/cl/_testgo/cgocfiles/cgocfiles.go index 5f346d275c..51d85ab1e8 100644 --- a/cl/_testgo/cgocfiles/cgocfiles.go +++ b/cl/_testgo/cgocfiles/cgocfiles.go @@ -9,12 +9,11 @@ import "fmt" // CHECK-LABEL: define i32 @main._Cfunc_test_structs(ptr %0, ptr %1, ptr %2, ptr %3, ptr %4){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %5 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_test_structs, align 8 -// CHECK-NEXT: %6 = load ptr, ptr %5, align 8 -// CHECK-NEXT: %7 = call i32 %6(ptr %0, ptr %1, ptr %2, ptr %3, ptr %4) -// CHECK-NEXT: ret i32 %7 +// CHECK-NEXT: %5 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %6 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_test_structs, align 8 +// CHECK-NEXT: %7 = load ptr, ptr %6, align 8 +// CHECK-NEXT: %8 = call i32 %7(ptr %0, ptr %1, ptr %2, ptr %3, ptr %4) +// CHECK-NEXT: ret i32 %8 // CHECK-NEXT: } // CHECK-LABEL: define void @main.main(){{.*}} { diff --git a/cl/_testgo/cgodefer/cgodefer.go b/cl/_testgo/cgodefer/cgodefer.go index ee04924ebf..8d9bca1306 100644 --- a/cl/_testgo/cgodefer/cgodefer.go +++ b/cl/_testgo/cgodefer/cgodefer.go @@ -8,12 +8,11 @@ import "C" // CHECK-LABEL: define [0 x i8] @main._Cfunc_free(ptr %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: %3 = call [0 x i8] %2(ptr %0) -// CHECK-NEXT: ret [0 x i8] %3 +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 +// CHECK-NEXT: %4 = call [0 x i8] %3(ptr %0) +// CHECK-NEXT: ret [0 x i8] %4 // CHECK-NEXT: } // CHECK-LABEL: define void @main.main(){{.*}} { diff --git a/cl/_testgo/cgomacro/cgomacro.go b/cl/_testgo/cgomacro/cgomacro.go index 701ab9b7e5..c2c152d37d 100644 --- a/cl/_testgo/cgomacro/cgomacro.go +++ b/cl/_testgo/cgomacro/cgomacro.go @@ -20,12 +20,11 @@ import ( // CHECK-LABEL: define i32 @main._Cfunc_PyObject_Print(ptr %0, ptr %1, i32 %2){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %3 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyObject_Print, align 8 -// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 -// CHECK-NEXT: %5 = call i32 %4(ptr %0, ptr %1, i32 %2) -// CHECK-NEXT: ret i32 %5 +// CHECK-NEXT: %3 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %4 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyObject_Print, align 8 +// CHECK-NEXT: %5 = load ptr, ptr %4, align 8 +// CHECK-NEXT: %6 = call i32 %5(ptr %0, ptr %1, i32 %2) +// CHECK-NEXT: ret i32 %6 // CHECK-NEXT: } // CHECK-LABEL: define [0 x i8] @main._Cfunc_Py_Finalize(){{.*}} { @@ -46,12 +45,11 @@ import ( // CHECK-LABEL: define i32 @main._Cfunc_fputs(ptr %0, ptr %1){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_fputs, align 8 -// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 -// CHECK-NEXT: %4 = call i32 %3(ptr %0, ptr %1) -// CHECK-NEXT: ret i32 %4 +// CHECK-NEXT: %2 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %3 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_fputs, align 8 +// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 +// CHECK-NEXT: %5 = call i32 %4(ptr %0, ptr %1) +// CHECK-NEXT: ret i32 %5 // CHECK-NEXT: } // CHECK-LABEL: define [0 x i8] @main._Cfunc_test_stdout(){{.*}} { @@ -64,38 +62,34 @@ import ( // CHECK-LABEL: define ptr @main._Cmacro_Py_False(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_False, align 8 -// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 -// CHECK-NEXT: ret ptr %1 +// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_False, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: ret ptr %2 // CHECK-NEXT: } // CHECK-LABEL: define ptr @main._Cmacro_Py_None(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_None, align 8 -// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 -// CHECK-NEXT: ret ptr %1 +// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_None, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: ret ptr %2 // CHECK-NEXT: } // CHECK-LABEL: define ptr @main._Cmacro_Py_True(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_True, align 8 -// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 -// CHECK-NEXT: ret ptr %1 +// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_True, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: ret ptr %2 // CHECK-NEXT: } // CHECK-LABEL: define ptr @main._Cmacro_stdout(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_stdout, align 8 -// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 -// CHECK-NEXT: ret ptr %1 +// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_stdout, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: ret ptr %2 // CHECK-NEXT: } // CHECK-LABEL: define void @main.init(){{.*}} { diff --git a/cl/_testgo/cgopython/cgopython.go b/cl/_testgo/cgopython/cgopython.go index d3c756558f..aea2a0f615 100644 --- a/cl/_testgo/cgopython/cgopython.go +++ b/cl/_testgo/cgopython/cgopython.go @@ -9,12 +9,11 @@ import "C" // CHECK-LABEL: define i32 @main._Cfunc_PyRun_SimpleString(ptr %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyRun_SimpleString, align 8 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: %3 = call i32 %2(ptr %0) -// CHECK-NEXT: ret i32 %3 +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyRun_SimpleString, align 8 +// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 +// CHECK-NEXT: %4 = call i32 %3(ptr %0) +// CHECK-NEXT: ret i32 %4 // CHECK-NEXT: } // CHECK-LABEL: define [0 x i8] @main._Cfunc_Py_Finalize(){{.*}} { diff --git a/cl/_testgo/cursor/in.go b/cl/_testgo/cursor/in.go index 935e2182a3..d0a2fc24bf 100644 --- a/cl/_testgo/cursor/in.go +++ b/cl/_testgo/cursor/in.go @@ -798,11 +798,10 @@ const ( // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 16, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) -// CHECK-NEXT: %0 = getelementptr inbounds %main.Cursor, ptr %.stack, i32 0, i32 0 -// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) -// CHECK-NEXT: store ptr %1, ptr %0, align 8 +// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) +// CHECK-NEXT: %1 = getelementptr inbounds %main.Cursor, ptr %0, i32 0, i32 0 +// CHECK-NEXT: %2 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// CHECK-NEXT: store ptr %2, ptr %1, align 8 // CHECK-NEXT: ret void // CHECK-NEXT: } diff --git a/cl/_testgo/returnorder/in.go b/cl/_testgo/returnorder/in.go index a3919d1efc..de98fd2b9b 100644 --- a/cl/_testgo/returnorder/in.go +++ b/cl/_testgo/returnorder/in.go @@ -28,15 +28,14 @@ func main() { // CHECK-LABEL: define { %main.state, i64 } @main.returnStateAndMut(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %0 = getelementptr inbounds %main.state, ptr %.stack, i32 0, i32 0 -// CHECK-NEXT: store i64 1, ptr %0, align 8 -// CHECK-NEXT: %1 = call i64 @"main.(*state).mutate"(ptr %.stack, i64 2) -// CHECK-NEXT: %2 = load %main.state, ptr %.stack, align 8 -// CHECK-NEXT: %3 = insertvalue { %main.state, i64 } undef, %main.state %2, 0 -// CHECK-NEXT: %4 = insertvalue { %main.state, i64 } %3, i64 %1, 1 -// CHECK-NEXT: ret { %main.state, i64 } %4 +// CHECK-NEXT: %0 = call ptr @"{{.*}}AllocZ"(i64 8) +// CHECK-NEXT: %1 = getelementptr inbounds %main.state, ptr %0, i32 0, i32 0 +// CHECK-NEXT: store i64 1, ptr %1, align 8 +// CHECK-NEXT: %2 = call i64 @"main.(*state).mutate"(ptr %0, i64 2) +// CHECK-NEXT: %3 = load %main.state, ptr %0, align 8 +// CHECK-NEXT: %4 = insertvalue { %main.state, i64 } undef, %main.state %3, 0 +// CHECK-NEXT: %5 = insertvalue { %main.state, i64 } %4, i64 %2, 1 +// CHECK-NEXT: ret { %main.state, i64 } %5 func returnStateAndMut() (state, int) { x := state{v: 1} return x, x.mutate(2) diff --git a/cl/_testgo/tprecurfn/in.go b/cl/_testgo/tprecurfn/in.go index 08c3ae7f8e..de9928916d 100644 --- a/cl/_testgo/tprecurfn/in.go +++ b/cl/_testgo/tprecurfn/in.go @@ -8,20 +8,19 @@ type My[T any] struct { // CHECK-LABEL: define void @main.main(){{.*}} { func main() { - // CHECK: %.stack = alloca i8, i64 24, align 8 - // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) - // CHECK-NEXT: %0 = getelementptr inbounds %"main.My[int]", ptr %.stack, i32 0, i32 1 - // CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) - // CHECK-NEXT: %2 = getelementptr inbounds %"main.My[int]", ptr %1, i32 0, i32 0 - // CHECK-NEXT: store { ptr, ptr } { ptr @"__llgo_stub.main.main$1", ptr null }, ptr %2, align 8 - // CHECK-NEXT: store ptr %1, ptr %0, align 8 - // CHECK-NEXT: %3 = getelementptr inbounds %"main.My[int]", ptr %.stack, i32 0, i32 1 - // CHECK-NEXT: %4 = load ptr, ptr %3, align 8 - // CHECK-NEXT: %5 = getelementptr inbounds %"main.My[int]", ptr %4, i32 0, i32 0 - // CHECK-NEXT: %6 = load { ptr, ptr }, ptr %5, align 8 - // CHECK-NEXT: %7 = extractvalue { ptr, ptr } %6, 1 - // CHECK-NEXT: %8 = extractvalue { ptr, ptr } %6, 0 - // CHECK-NEXT: call void %8(ptr %7, i64 100) + // CHECK: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) + // CHECK-NEXT: %1 = getelementptr inbounds %"main.My[int]", ptr %0, i32 0, i32 1 + // CHECK-NEXT: %2 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) + // CHECK-NEXT: %3 = getelementptr inbounds %"main.My[int]", ptr %2, i32 0, i32 0 + // CHECK-NEXT: store { ptr, ptr } { ptr @"__llgo_stub.main.main$1", ptr null }, ptr %3, align 8 + // CHECK-NEXT: store ptr %2, ptr %1, align 8 + // CHECK-NEXT: %4 = getelementptr inbounds %"main.My[int]", ptr %0, i32 0, i32 1 + // CHECK-NEXT: %5 = load ptr, ptr %4, align 8 + // CHECK-NEXT: %6 = getelementptr inbounds %"main.My[int]", ptr %5, i32 0, i32 0 + // CHECK-NEXT: %7 = load { ptr, ptr }, ptr %6, align 8 + // CHECK-NEXT: %8 = extractvalue { ptr, ptr } %7, 1 + // CHECK-NEXT: %9 = extractvalue { ptr, ptr } %7, 0 + // CHECK-NEXT: call void %9(ptr %8, i64 100) // CHECK-NEXT: ret void // CHECK-NEXT:} m := &My[int]{next: &My[int]{fn: func(n int) { println(n) }}} diff --git a/cl/_testgo/tptypes/in.go b/cl/_testgo/tptypes/in.go index b300c773a1..c6130848bd 100644 --- a/cl/_testgo/tptypes/in.go +++ b/cl/_testgo/tptypes/in.go @@ -89,93 +89,90 @@ type ( // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 0) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %.stack = alloca i8, i64 24, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) -// CHECK-NEXT: %16 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %17 = getelementptr inbounds i64, ptr %16, i64 0 -// CHECK-NEXT: store i64 100, ptr %17, align 8 -// CHECK-NEXT: %18 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %16, 0 -// CHECK-NEXT: %19 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %18, i64 1, 1 -// CHECK-NEXT: %20 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %19, i64 1, 2 -// CHECK-NEXT: %21 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %.stack, %"{{.*}}/runtime/internal/runtime.Slice" %20) -// CHECK-NEXT: %.stack1 = alloca i8, i64 24, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 24, i1 false) -// CHECK-NEXT: %22 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) -// CHECK-NEXT: %23 = getelementptr inbounds %"{{.*}}/runtime/internal/runtime.String", ptr %22, i64 0 -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 5 }, ptr %23, align 8 -// CHECK-NEXT: %24 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %22, 0 -// CHECK-NEXT: %25 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %24, i64 1, 1 -// CHECK-NEXT: %26 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %25, i64 1, 2 -// CHECK-NEXT: %27 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]string,string\]}}).Append"(ptr %.stack1, %"{{.*}}/runtime/internal/runtime.Slice" %26) -// CHECK-NEXT: %.stack2 = alloca i8, i64 24, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack2, i8 0, i64 24, i1 false) -// CHECK-NEXT: %28 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) -// CHECK-NEXT: %29 = getelementptr inbounds i64, ptr %28, i64 0 -// CHECK-NEXT: store i64 1, ptr %29, align 8 -// CHECK-NEXT: %30 = getelementptr inbounds i64, ptr %28, i64 1 -// CHECK-NEXT: store i64 2, ptr %30, align 8 -// CHECK-NEXT: %31 = getelementptr inbounds i64, ptr %28, i64 2 -// CHECK-NEXT: store i64 3, ptr %31, align 8 -// CHECK-NEXT: %32 = getelementptr inbounds i64, ptr %28, i64 3 -// CHECK-NEXT: store i64 4, ptr %32, align 8 -// CHECK-NEXT: %33 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %28, 0 -// CHECK-NEXT: %34 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %33, i64 4, 1 -// CHECK-NEXT: %35 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %34, i64 4, 2 -// CHECK-NEXT: %36 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %.stack2, %"{{.*}}/runtime/internal/runtime.Slice" %35) -// CHECK-NEXT: %37 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) -// CHECK-NEXT: %38 = getelementptr inbounds i64, ptr %37, i64 0 -// CHECK-NEXT: store i64 1, ptr %38, align 8 -// CHECK-NEXT: %39 = getelementptr inbounds i64, ptr %37, i64 1 -// CHECK-NEXT: store i64 2, ptr %39, align 8 -// CHECK-NEXT: %40 = getelementptr inbounds i64, ptr %37, i64 2 -// CHECK-NEXT: store i64 3, ptr %40, align 8 -// CHECK-NEXT: %41 = getelementptr inbounds i64, ptr %37, i64 3 -// CHECK-NEXT: store i64 4, ptr %41, align 8 -// CHECK-NEXT: %42 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %37, 0 -// CHECK-NEXT: %43 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %42, i64 4, 1 -// CHECK-NEXT: %44 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %43, i64 4, 2 -// CHECK-NEXT: %45 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append2"(ptr %.stack2, %"{{.*}}/runtime/internal/runtime.Slice" %44) -// CHECK-NEXT: %46 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack, i32 0, i32 0 -// CHECK-NEXT: %47 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %46, align 8 -// CHECK-NEXT: %48 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack, i32 0, i32 0 -// CHECK-NEXT: %49 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %48, align 8 -// CHECK-NEXT: %50 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %49, 0 -// CHECK-NEXT: %51 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %49, 1 -// CHECK-NEXT: %52 = icmp uge i64 0, %51 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %52, {{.*}}) -// CHECK-NEXT: %53 = getelementptr inbounds i64, ptr %50, i64 0 -// CHECK-NEXT: %54 = load i64, ptr %53, align 8 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %47) +// CHECK-NEXT: %16 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// CHECK-NEXT: %17 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %18 = getelementptr inbounds i64, ptr %17, i64 0 +// CHECK-NEXT: store i64 100, ptr %18, align 8 +// CHECK-NEXT: %19 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %17, 0 +// CHECK-NEXT: %20 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %19, i64 1, 1 +// CHECK-NEXT: %21 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %20, i64 1, 2 +// CHECK-NEXT: %22 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %16, %"{{.*}}/runtime/internal/runtime.Slice" %21) +// CHECK-NEXT: %23 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// CHECK-NEXT: %24 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) +// CHECK-NEXT: %25 = getelementptr inbounds %"{{.*}}/runtime/internal/runtime.String", ptr %24, i64 0 +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 5 }, ptr %25, align 8 +// CHECK-NEXT: %26 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %24, 0 +// CHECK-NEXT: %27 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %26, i64 1, 1 +// CHECK-NEXT: %28 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %27, i64 1, 2 +// CHECK-NEXT: %29 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]string,string\]}}).Append"(ptr %23, %"{{.*}}/runtime/internal/runtime.Slice" %28) +// CHECK-NEXT: %30 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// CHECK-NEXT: %31 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// CHECK-NEXT: %32 = getelementptr inbounds i64, ptr %31, i64 0 +// CHECK-NEXT: store i64 1, ptr %32, align 8 +// CHECK-NEXT: %33 = getelementptr inbounds i64, ptr %31, i64 1 +// CHECK-NEXT: store i64 2, ptr %33, align 8 +// CHECK-NEXT: %34 = getelementptr inbounds i64, ptr %31, i64 2 +// CHECK-NEXT: store i64 3, ptr %34, align 8 +// CHECK-NEXT: %35 = getelementptr inbounds i64, ptr %31, i64 3 +// CHECK-NEXT: store i64 4, ptr %35, align 8 +// CHECK-NEXT: %36 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %31, 0 +// CHECK-NEXT: %37 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %36, i64 4, 1 +// CHECK-NEXT: %38 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %37, i64 4, 2 +// CHECK-NEXT: %39 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %30, %"{{.*}}/runtime/internal/runtime.Slice" %38) +// CHECK-NEXT: %40 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// CHECK-NEXT: %41 = getelementptr inbounds i64, ptr %40, i64 0 +// CHECK-NEXT: store i64 1, ptr %41, align 8 +// CHECK-NEXT: %42 = getelementptr inbounds i64, ptr %40, i64 1 +// CHECK-NEXT: store i64 2, ptr %42, align 8 +// CHECK-NEXT: %43 = getelementptr inbounds i64, ptr %40, i64 2 +// CHECK-NEXT: store i64 3, ptr %43, align 8 +// CHECK-NEXT: %44 = getelementptr inbounds i64, ptr %40, i64 3 +// CHECK-NEXT: store i64 4, ptr %44, align 8 +// CHECK-NEXT: %45 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %40, 0 +// CHECK-NEXT: %46 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %45, i64 4, 1 +// CHECK-NEXT: %47 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %46, i64 4, 2 +// CHECK-NEXT: %48 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append2"(ptr %30, %"{{.*}}/runtime/internal/runtime.Slice" %47) +// CHECK-NEXT: %49 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %16, i32 0, i32 0 +// CHECK-NEXT: %50 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %49, align 8 +// CHECK-NEXT: %51 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %16, i32 0, i32 0 +// CHECK-NEXT: %52 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %51, align 8 +// CHECK-NEXT: %53 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %52, 0 +// CHECK-NEXT: %54 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %52, 1 +// CHECK-NEXT: %55 = icmp uge i64 0, %54 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %55, {{.*}}) +// CHECK-NEXT: %56 = getelementptr inbounds i64, ptr %53, i64 0 +// CHECK-NEXT: %57 = load i64, ptr %56, align 8 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %50) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %54) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %57) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %55 = getelementptr inbounds %"main.Slice{{\[\[\]string,string\]}}", ptr %.stack1, i32 0, i32 0 -// CHECK-NEXT: %56 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %55, align 8 -// CHECK-NEXT: %57 = getelementptr inbounds %"main.Slice{{\[\[\]string,string\]}}", ptr %.stack1, i32 0, i32 0 -// CHECK-NEXT: %58 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %57, align 8 -// CHECK-NEXT: %59 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %58, 0 -// CHECK-NEXT: %60 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %58, 1 -// CHECK-NEXT: %61 = icmp uge i64 0, %60 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %61, {{.*}}) -// CHECK-NEXT: %62 = getelementptr inbounds %"{{.*}}/runtime/internal/runtime.String", ptr %59, i64 0 -// CHECK-NEXT: %63 = load %"{{.*}}/runtime/internal/runtime.String", ptr %62, align 8 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %56) +// CHECK-NEXT: %58 = getelementptr inbounds %"main.Slice{{\[\[\]string,string\]}}", ptr %23, i32 0, i32 0 +// CHECK-NEXT: %59 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %58, align 8 +// CHECK-NEXT: %60 = getelementptr inbounds %"main.Slice{{\[\[\]string,string\]}}", ptr %23, i32 0, i32 0 +// CHECK-NEXT: %61 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %60, align 8 +// CHECK-NEXT: %62 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %61, 0 +// CHECK-NEXT: %63 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %61, 1 +// CHECK-NEXT: %64 = icmp uge i64 0, %63 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %64, {{.*}}) +// CHECK-NEXT: %65 = getelementptr inbounds %"{{.*}}/runtime/internal/runtime.String", ptr %62, i64 0 +// CHECK-NEXT: %66 = load %"{{.*}}/runtime/internal/runtime.String", ptr %65, align 8 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %59) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %63) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %66) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %64 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack2, i32 0, i32 0 -// CHECK-NEXT: %65 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %64, align 8 -// CHECK-NEXT: %66 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack2, i32 0, i32 0 -// CHECK-NEXT: %67 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %66, align 8 -// CHECK-NEXT: %68 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %67, 0 -// CHECK-NEXT: %69 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %67, 1 -// CHECK-NEXT: %70 = icmp uge i64 0, %69 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %70, {{.*}}) -// CHECK-NEXT: %71 = getelementptr inbounds i64, ptr %68, i64 0 -// CHECK-NEXT: %72 = load i64, ptr %71, align 8 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %65) +// CHECK-NEXT: %67 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %30, i32 0, i32 0 +// CHECK-NEXT: %68 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %67, align 8 +// CHECK-NEXT: %69 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %30, i32 0, i32 0 +// CHECK-NEXT: %70 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %69, align 8 +// CHECK-NEXT: %71 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %70, 0 +// CHECK-NEXT: %72 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %70, 1 +// CHECK-NEXT: %73 = icmp uge i64 0, %72 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %73, {{.*}}) +// CHECK-NEXT: %74 = getelementptr inbounds i64, ptr %71, i64 0 +// CHECK-NEXT: %75 = load i64, ptr %74, align 8 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %68) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %72) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %75) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: ret void // CHECK-NEXT: } diff --git a/cl/_testlibc/atomic/in.go b/cl/_testlibc/atomic/in.go index 3d272880d8..f1681d9c38 100644 --- a/cl/_testlibc/atomic/in.go +++ b/cl/_testlibc/atomic/in.go @@ -10,23 +10,23 @@ import ( func main() { var v int64 - // CHECK: store atomic i64 100, ptr %.stack seq_cst, align 8 + // CHECK: store atomic i64 100, ptr %0 seq_cst, align 8 atomic.Store(&v, 100) - // CHECK: %0 = load atomic i64, ptr %.stack seq_cst, align 8 + // CHECK: %1 = load atomic i64, ptr %0 seq_cst, align 8 c.Printf(c.Str("store: %ld\n"), atomic.Load(&v)) - // CHECK: %2 = atomicrmw add ptr %.stack, i64 1 seq_cst, align 8 + // CHECK: %3 = atomicrmw add ptr %0, i64 1 seq_cst, align 8 ret := atomic.Add(&v, 1) c.Printf(c.Str("ret: %ld, v: %ld\n"), ret, v) - // CHECK: %5 = cmpxchg ptr %.stack, i64 100, i64 102 seq_cst seq_cst, align 8 + // CHECK: %6 = cmpxchg ptr %0, i64 100, i64 102 seq_cst seq_cst, align 8 ret, _ = atomic.CompareAndExchange(&v, 100, 102) c.Printf(c.Str("ret: %ld vs 100, v: %ld\n"), ret, v) - // CHECK: %10 = cmpxchg ptr %.stack, i64 101, i64 102 seq_cst seq_cst, align 8 + // CHECK: %11 = cmpxchg ptr %0, i64 101, i64 102 seq_cst seq_cst, align 8 ret, _ = atomic.CompareAndExchange(&v, 101, 102) c.Printf(c.Str("ret: %ld vs 101, v: %ld\n"), ret, v) - // CHECK: %15 = atomicrmw sub ptr %.stack, i64 1 seq_cst, align 8 + // CHECK: %16 = atomicrmw sub ptr %0, i64 1 seq_cst, align 8 ret = atomic.Sub(&v, 1) c.Printf(c.Str("ret: %ld, v: %ld\n"), ret, v) } diff --git a/cl/_testlibgo/atomic/in.go b/cl/_testlibgo/atomic/in.go index e4bb927bde..d97635e548 100644 --- a/cl/_testlibgo/atomic/in.go +++ b/cl/_testlibgo/atomic/in.go @@ -9,32 +9,32 @@ import ( func main() { var v int64 - // CHECK: store atomic i64 100, ptr %.stack seq_cst, align 8 - // CHECK: %0 = load atomic i64, ptr %.stack seq_cst, align 8 + // CHECK: store atomic i64 100, ptr %0 seq_cst, align 8 + // CHECK: %1 = load atomic i64, ptr %0 seq_cst, align 8 atomic.StoreInt64(&v, 100) println("store:", atomic.LoadInt64(&v)) - // CHECK: %1 = atomicrmw add ptr %.stack, i64 1 seq_cst, align 8 - // CHECK: %2 = add i64 %1, 1 - // CHECK: %3 = load i64, ptr %.stack, align 8 + // CHECK: %2 = atomicrmw add ptr %0, i64 1 seq_cst, align 8 + // CHECK: %3 = add i64 %2, 1 + // CHECK: %4 = load i64, ptr %0, align 8 ret := atomic.AddInt64(&v, 1) println("ret:", ret, "v:", v) - // CHECK: %4 = cmpxchg ptr %.stack, i64 100, i64 102 seq_cst seq_cst, align 8 - // CHECK: %5 = extractvalue { i64, i1 } %4, 1 - // CHECK: %6 = load i64, ptr %.stack, align 8 + // CHECK: %5 = cmpxchg ptr %0, i64 100, i64 102 seq_cst seq_cst, align 8 + // CHECK: %6 = extractvalue { i64, i1 } %5, 1 + // CHECK: %7 = load i64, ptr %0, align 8 swp := atomic.CompareAndSwapInt64(&v, 100, 102) println("swp:", swp, "v:", v) - // CHECK: %7 = cmpxchg ptr %.stack, i64 101, i64 102 seq_cst seq_cst, align 8 - // CHECK: %8 = extractvalue { i64, i1 } %7, 1 - // CHECK: %9 = load i64, ptr %.stack, align 8 + // CHECK: %8 = cmpxchg ptr %0, i64 101, i64 102 seq_cst seq_cst, align 8 + // CHECK: %9 = extractvalue { i64, i1 } %8, 1 + // CHECK: %10 = load i64, ptr %0, align 8 swp = atomic.CompareAndSwapInt64(&v, 101, 102) println("swp:", swp, "v:", v) - // CHECK: %10 = atomicrmw add ptr %.stack, i64 -1 seq_cst, align 8 - // CHECK: %11 = add i64 %10, -1 - // CHECK: %12 = load i64, ptr %.stack, align 8 + // CHECK: %11 = atomicrmw add ptr %0, i64 -1 seq_cst, align 8 + // CHECK: %12 = add i64 %11, -1 + // CHECK: %13 = load i64, ptr %0, align 8 ret = atomic.AddInt64(&v, -1) println("ret:", ret, "v:", v) } diff --git a/cl/_testrt/abitype/in.go b/cl/_testrt/abitype/in.go index 39b520e290..ced9c0c2d7 100644 --- a/cl/_testrt/abitype/in.go +++ b/cl/_testrt/abitype/in.go @@ -9,43 +9,42 @@ import ( // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 16, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) -// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 4) -// CHECK-NEXT: store i32 0, ptr %0, align 4 -// CHECK-NEXT: %1 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_int32, ptr undef }, ptr %0, 1 -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %1, ptr %.stack, align 8 -// CHECK-NEXT: %2 = getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 -// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 -// CHECK-NEXT: %4 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/abi.(*Type).String"(ptr %3) -// CHECK-NEXT: %5 = call i1 @"{{.*}}/runtime/internal/runtime.StringEqual"(%"{{.*}}/runtime/internal/runtime.String" %4, %"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 5 }) -// CHECK-NEXT: %6 = xor i1 %5, true -// CHECK-NEXT: br i1 %6, label %_llgo_1, label %_llgo_2 +// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 4) +// CHECK-NEXT: store i32 0, ptr %1, align 4 +// CHECK-NEXT: %2 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_int32, ptr undef }, ptr %1, 1 +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %2, ptr %0, align 8 +// CHECK-NEXT: %3 = getelementptr inbounds %main.eface, ptr %0, i32 0, i32 0 +// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 +// CHECK-NEXT: %5 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/abi.(*Type).String"(ptr %4) +// CHECK-NEXT: %6 = call i1 @"{{.*}}/runtime/internal/runtime.StringEqual"(%"{{.*}}/runtime/internal/runtime.String" %5, %"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 5 }) +// CHECK-NEXT: %7 = xor i1 %6, true +// CHECK-NEXT: br i1 %7, label %_llgo_1, label %_llgo_2 // CHECK-EMPTY: // CHECK-NEXT: _llgo_1: ; preds = %_llgo_0 -// CHECK-NEXT: %7 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @1, i64 14 }, ptr %7, align 8 -// CHECK-NEXT: %8 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %7, 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %8) +// CHECK-NEXT: %8 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @1, i64 14 }, ptr %8, align 8 +// CHECK-NEXT: %9 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %8, 1 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %9) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_2: ; preds = %_llgo_0 -// CHECK-NEXT: %9 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 1) -// CHECK-NEXT: store i8 0, ptr %9, align 1 -// CHECK-NEXT: %10 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_uint8, ptr undef }, ptr %9, 1 -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %10, ptr %.stack, align 8 -// CHECK-NEXT: %11 = getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 -// CHECK-NEXT: %12 = load ptr, ptr %11, align 8 -// CHECK-NEXT: %13 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/abi.(*Type).String"(ptr %12) -// CHECK-NEXT: %14 = call i1 @"{{.*}}/runtime/internal/runtime.StringEqual"(%"{{.*}}/runtime/internal/runtime.String" %13, %"{{.*}}/runtime/internal/runtime.String" { ptr @3, i64 5 }) -// CHECK-NEXT: %15 = xor i1 %14, true -// CHECK-NEXT: br i1 %15, label %_llgo_3, label %_llgo_4 +// CHECK-NEXT: %10 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 1) +// CHECK-NEXT: store i8 0, ptr %10, align 1 +// CHECK-NEXT: %11 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_uint8, ptr undef }, ptr %10, 1 +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %11, ptr %0, align 8 +// CHECK-NEXT: %12 = getelementptr inbounds %main.eface, ptr %0, i32 0, i32 0 +// CHECK-NEXT: %13 = load ptr, ptr %12, align 8 +// CHECK-NEXT: %14 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/abi.(*Type).String"(ptr %13) +// CHECK-NEXT: %15 = call i1 @"{{.*}}/runtime/internal/runtime.StringEqual"(%"{{.*}}/runtime/internal/runtime.String" %14, %"{{.*}}/runtime/internal/runtime.String" { ptr @3, i64 5 }) +// CHECK-NEXT: %16 = xor i1 %15, true +// CHECK-NEXT: br i1 %16, label %_llgo_3, label %_llgo_4 // CHECK-EMPTY: // CHECK-NEXT: _llgo_3: ; preds = %_llgo_2 -// CHECK-NEXT: %16 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @4, i64 14 }, ptr %16, align 8 -// CHECK-NEXT: %17 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %16, 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %17) +// CHECK-NEXT: %17 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @4, i64 14 }, ptr %17, align 8 +// CHECK-NEXT: %18 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %17, 1 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %18) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_4: ; preds = %_llgo_2 diff --git a/cl/_testrt/builtin/in.go b/cl/_testrt/builtin/in.go index 9add030e07..fdc7ff8da0 100644 --- a/cl/_testrt/builtin/in.go +++ b/cl/_testrt/builtin/in.go @@ -31,11 +31,10 @@ const ( // CHECK-LABEL: define double @main.Float64frombits(i64 %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: store i64 %0, ptr %.stack, align 8 -// CHECK-NEXT: %1 = load double, ptr %.stack, align 8 -// CHECK-NEXT: ret double %1 +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: store i64 %0, ptr %1, align 8 +// CHECK-NEXT: %2 = load double, ptr %1, align 8 +// CHECK-NEXT: ret double %2 // CHECK-NEXT: } func Float64frombits(b uint64) float64 { return *(*float64)(unsafe.Pointer(&b)) } diff --git a/cl/_testrt/eface/in.go b/cl/_testrt/eface/in.go index a4a246d686..68b29347b3 100644 --- a/cl/_testrt/eface/in.go +++ b/cl/_testrt/eface/in.go @@ -19,12 +19,11 @@ func (t *T) Invoke() { // CHECK-LABEL: define void @main.dump(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 16, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 -// CHECK-NEXT: %1 = getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: call void @main.dumpTyp(ptr %2, %"{{.*}}/runtime/internal/runtime.String" zeroinitializer) +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %1, align 8 +// CHECK-NEXT: %2 = getelementptr inbounds %main.eface, ptr %1, i32 0, i32 0 +// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 +// CHECK-NEXT: call void @main.dumpTyp(ptr %3, %"{{.*}}/runtime/internal/runtime.String" zeroinitializer) // CHECK-NEXT: ret void // CHECK-NEXT: } func dump(v any) { diff --git a/cl/_testrt/funcaddr/in.go b/cl/_testrt/funcaddr/in.go index 46d0514e9c..6457b31b93 100644 --- a/cl/_testrt/funcaddr/in.go +++ b/cl/_testrt/funcaddr/in.go @@ -21,25 +21,23 @@ func add(a, b int) int { // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: store ptr @main.add, ptr %.stack, align 8 -// CHECK-NEXT: %.stack1 = alloca i8, i64 8, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 8, i1 false) -// CHECK-NEXT: store ptr @"main.main$1", ptr %.stack1, align 8 -// CHECK-NEXT: %0 = load ptr, ptr %.stack, align 8 -// CHECK-NEXT: %1 = icmp eq ptr @main.add, %0 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %1) +// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: store ptr @main.add, ptr %0, align 8 +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: store ptr @"main.main$1", ptr %1, align 8 +// CHECK-NEXT: %2 = load ptr, ptr %0, align 8 +// CHECK-NEXT: %3 = icmp eq ptr @main.add, %2 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %3) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %2 = load ptr, ptr %.stack, align 8 -// CHECK-NEXT: %3 = load ptr, ptr %.stack, align 8 -// CHECK-NEXT: %4 = icmp eq ptr %2, %3 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %4) +// CHECK-NEXT: %4 = load ptr, ptr %0, align 8 +// CHECK-NEXT: %5 = load ptr, ptr %0, align 8 +// CHECK-NEXT: %6 = icmp eq ptr %4, %5 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %6) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %5 = load ptr, ptr %.stack1, align 8 -// CHECK-NEXT: %6 = load ptr, ptr %.stack1, align 8 -// CHECK-NEXT: %7 = icmp eq ptr %5, %6 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %7) +// CHECK-NEXT: %7 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %8 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %9 = icmp eq ptr %7, %8 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %9) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: ret void // CHECK-NEXT: } diff --git a/cl/_testrt/funcdecl/in.go b/cl/_testrt/funcdecl/in.go index 8fe57dda4d..8efd3f4635 100644 --- a/cl/_testrt/funcdecl/in.go +++ b/cl/_testrt/funcdecl/in.go @@ -73,14 +73,13 @@ func check(fn func()) { // CHECK-LABEL: define ptr @main.closurePtr(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 16, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 -// CHECK-NEXT: %1 = getelementptr inbounds %main.rtype, ptr %.stack, i32 0, i32 1 -// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 -// CHECK-NEXT: %3 = getelementptr inbounds { ptr, ptr }, ptr %2, i32 0, i32 0 -// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 -// CHECK-NEXT: ret ptr %4 +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %1, align 8 +// CHECK-NEXT: %2 = getelementptr inbounds %main.rtype, ptr %1, i32 0, i32 1 +// CHECK-NEXT: %3 = load ptr, ptr %2, align 8 +// CHECK-NEXT: %4 = getelementptr inbounds { ptr, ptr }, ptr %3, i32 0, i32 0 +// CHECK-NEXT: %5 = load ptr, ptr %4, align 8 +// CHECK-NEXT: ret ptr %5 // CHECK-NEXT: } func closurePtr(a any) unsafe.Pointer { diff --git a/cl/_testrt/index/in.go b/cl/_testrt/index/in.go index 0aea23a80c..8f19ac4768 100644 --- a/cl/_testrt/index/in.go +++ b/cl/_testrt/index/in.go @@ -119,35 +119,34 @@ type S []int // CHECK-NEXT: %51 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/internal/runtime.StringFromUint64"(i64 %50) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %51) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %.stack = alloca i8, i64 16, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) -// CHECK-NEXT: %52 = getelementptr inbounds i64, ptr %.stack, i64 0 -// CHECK-NEXT: %53 = getelementptr inbounds i64, ptr %.stack, i64 1 -// CHECK-NEXT: store i64 1, ptr %52, align 8 -// CHECK-NEXT: store i64 2, ptr %53, align 8 -// CHECK-NEXT: %54 = getelementptr inbounds i64, ptr %.stack, i64 1 -// CHECK-NEXT: %55 = load i64, ptr %54, align 8 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %55) +// CHECK-NEXT: %52 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) +// CHECK-NEXT: %53 = getelementptr inbounds i64, ptr %52, i64 0 +// CHECK-NEXT: %54 = getelementptr inbounds i64, ptr %52, i64 1 +// CHECK-NEXT: store i64 1, ptr %53, align 8 +// CHECK-NEXT: store i64 2, ptr %54, align 8 +// CHECK-NEXT: %55 = getelementptr inbounds i64, ptr %52, i64 1 +// CHECK-NEXT: %56 = load i64, ptr %55, align 8 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %56) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %56 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) -// CHECK-NEXT: %57 = getelementptr inbounds i64, ptr %56, i64 0 -// CHECK-NEXT: store i64 1, ptr %57, align 8 -// CHECK-NEXT: %58 = getelementptr inbounds i64, ptr %56, i64 1 -// CHECK-NEXT: store i64 2, ptr %58, align 8 -// CHECK-NEXT: %59 = getelementptr inbounds i64, ptr %56, i64 2 -// CHECK-NEXT: store i64 3, ptr %59, align 8 -// CHECK-NEXT: %60 = getelementptr inbounds i64, ptr %56, i64 3 -// CHECK-NEXT: store i64 4, ptr %60, align 8 -// CHECK-NEXT: %61 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %56, 0 -// CHECK-NEXT: %62 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %61, i64 4, 1 -// CHECK-NEXT: %63 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %62, i64 4, 2 -// CHECK-NEXT: %64 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %63, 0 -// CHECK-NEXT: %65 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %63, 1 -// CHECK-NEXT: %66 = icmp uge i64 1, %65 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %66, i64 1, i1 true, i64 %65) -// CHECK-NEXT: %67 = getelementptr inbounds i64, ptr %64, i64 1 -// CHECK-NEXT: %68 = load i64, ptr %67, align 8 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %68) +// CHECK-NEXT: %57 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// CHECK-NEXT: %58 = getelementptr inbounds i64, ptr %57, i64 0 +// CHECK-NEXT: store i64 1, ptr %58, align 8 +// CHECK-NEXT: %59 = getelementptr inbounds i64, ptr %57, i64 1 +// CHECK-NEXT: store i64 2, ptr %59, align 8 +// CHECK-NEXT: %60 = getelementptr inbounds i64, ptr %57, i64 2 +// CHECK-NEXT: store i64 3, ptr %60, align 8 +// CHECK-NEXT: %61 = getelementptr inbounds i64, ptr %57, i64 3 +// CHECK-NEXT: store i64 4, ptr %61, align 8 +// CHECK-NEXT: %62 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %57, 0 +// CHECK-NEXT: %63 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %62, i64 4, 1 +// CHECK-NEXT: %64 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %63, i64 4, 2 +// CHECK-NEXT: %65 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %64, 0 +// CHECK-NEXT: %66 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %64, 1 +// CHECK-NEXT: %67 = icmp uge i64 1, %66 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %67, i64 1, i1 true, i64 %66) +// CHECK-NEXT: %68 = getelementptr inbounds i64, ptr %65, i64 1 +// CHECK-NEXT: %69 = load i64, ptr %68, align 8 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %69) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 0) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) diff --git a/cl/_testrt/len/in.go b/cl/_testrt/len/in.go index 5d1ecae09c..3bd00461b1 100644 --- a/cl/_testrt/len/in.go +++ b/cl/_testrt/len/in.go @@ -10,93 +10,91 @@ type data struct { // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 56, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 56, i1 false) -// CHECK-NEXT: %0 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 0 -// CHECK-NEXT: %1 = load %"{{.*}}/runtime/internal/runtime.String", ptr %0, align 8 -// CHECK-NEXT: %2 = extractvalue %"{{.*}}/runtime/internal/runtime.String" %1, 1 -// CHECK-NEXT: %3 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 1 -// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 -// CHECK-NEXT: %5 = call i64 @"{{.*}}/runtime/internal/runtime.ChanLen"(ptr %4) -// CHECK-NEXT: %6 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 2 -// CHECK-NEXT: %7 = load ptr, ptr %6, align 8 -// CHECK-NEXT: %8 = call i64 @"{{.*}}/runtime/internal/runtime.MapLen"(ptr %7) -// CHECK-NEXT: %9 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 3 -// CHECK-NEXT: %10 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %9, align 8 -// CHECK-NEXT: %11 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %10, 1 -// CHECK-NEXT: %12 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 1 -// CHECK-NEXT: %13 = load ptr, ptr %12, align 8 -// CHECK-NEXT: %14 = call i64 @"{{.*}}/runtime/internal/runtime.ChanCap"(ptr %13) -// CHECK-NEXT: %15 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 3 -// CHECK-NEXT: %16 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %15, align 8 -// CHECK-NEXT: %17 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %16, 2 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %2) +// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 56) +// CHECK-NEXT: %1 = getelementptr inbounds %main.data, ptr %0, i32 0, i32 0 +// CHECK-NEXT: %2 = load %"{{.*}}/runtime/internal/runtime.String", ptr %1, align 8 +// CHECK-NEXT: %3 = extractvalue %"{{.*}}/runtime/internal/runtime.String" %2, 1 +// CHECK-NEXT: %4 = getelementptr inbounds %main.data, ptr %0, i32 0, i32 1 +// CHECK-NEXT: %5 = load ptr, ptr %4, align 8 +// CHECK-NEXT: %6 = call i64 @"{{.*}}/runtime/internal/runtime.ChanLen"(ptr %5) +// CHECK-NEXT: %7 = getelementptr inbounds %main.data, ptr %0, i32 0, i32 2 +// CHECK-NEXT: %8 = load ptr, ptr %7, align 8 +// CHECK-NEXT: %9 = call i64 @"{{.*}}/runtime/internal/runtime.MapLen"(ptr %8) +// CHECK-NEXT: %10 = getelementptr inbounds %main.data, ptr %0, i32 0, i32 3 +// CHECK-NEXT: %11 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %10, align 8 +// CHECK-NEXT: %12 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %11, 1 +// CHECK-NEXT: %13 = getelementptr inbounds %main.data, ptr %0, i32 0, i32 1 +// CHECK-NEXT: %14 = load ptr, ptr %13, align 8 +// CHECK-NEXT: %15 = call i64 @"{{.*}}/runtime/internal/runtime.ChanCap"(ptr %14) +// CHECK-NEXT: %16 = getelementptr inbounds %main.data, ptr %0, i32 0, i32 3 +// CHECK-NEXT: %17 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %16, align 8 +// CHECK-NEXT: %18 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %17, 2 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %3) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %5) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %6) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %8) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %9) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %11) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %12) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %14) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %15) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %17) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %18) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) -// CHECK-NEXT: %.stack1 = alloca i8, i64 56, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 56, i1 false) -// CHECK-NEXT: %18 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 0 -// CHECK-NEXT: %19 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 1 -// CHECK-NEXT: %20 = call ptr @"{{.*}}/runtime/internal/runtime.NewChan"(i64 8, i64 2) -// CHECK-NEXT: %21 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 2 -// CHECK-NEXT: %22 = call ptr @"{{.*}}/runtime/internal/runtime.MakeMap"(ptr @"map[_llgo_int]_llgo_string", i64 1) -// CHECK-NEXT: %23 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 8) -// CHECK-NEXT: store i64 1, ptr %23, align 8 -// CHECK-NEXT: %24 = call ptr @"{{.*}}/runtime/internal/runtime.MapAssign"(ptr @"map[_llgo_int]_llgo_string", ptr %22, ptr %23) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @16, i64 5 }, ptr %24, align 8 -// CHECK-NEXT: %25 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 3 -// CHECK-NEXT: %26 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) -// CHECK-NEXT: %27 = getelementptr inbounds i64, ptr %26, i64 0 -// CHECK-NEXT: store i64 1, ptr %27, align 8 -// CHECK-NEXT: %28 = getelementptr inbounds i64, ptr %26, i64 1 -// CHECK-NEXT: store i64 2, ptr %28, align 8 -// CHECK-NEXT: %29 = getelementptr inbounds i64, ptr %26, i64 2 -// CHECK-NEXT: store i64 3, ptr %29, align 8 -// CHECK-NEXT: %30 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %26, 0 -// CHECK-NEXT: %31 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %30, i64 3, 1 -// CHECK-NEXT: %32 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %31, i64 3, 2 -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @16, i64 5 }, ptr %18, align 8 -// CHECK-NEXT: store ptr %20, ptr %19, align 8 +// CHECK-NEXT: %19 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 56) +// CHECK-NEXT: %20 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 0 +// CHECK-NEXT: %21 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 1 +// CHECK-NEXT: %22 = call ptr @"{{.*}}/runtime/internal/runtime.NewChan"(i64 8, i64 2) +// CHECK-NEXT: %23 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 2 +// CHECK-NEXT: %24 = call ptr @"{{.*}}/runtime/internal/runtime.MakeMap"(ptr @"map[_llgo_int]_llgo_string", i64 1) +// CHECK-NEXT: %25 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 8) +// CHECK-NEXT: store i64 1, ptr %25, align 8 +// CHECK-NEXT: %26 = call ptr @"{{.*}}/runtime/internal/runtime.MapAssign"(ptr @"map[_llgo_int]_llgo_string", ptr %24, ptr %25) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @16, i64 5 }, ptr %26, align 8 +// CHECK-NEXT: %27 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 3 +// CHECK-NEXT: %28 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// CHECK-NEXT: %29 = getelementptr inbounds i64, ptr %28, i64 0 +// CHECK-NEXT: store i64 1, ptr %29, align 8 +// CHECK-NEXT: %30 = getelementptr inbounds i64, ptr %28, i64 1 +// CHECK-NEXT: store i64 2, ptr %30, align 8 +// CHECK-NEXT: %31 = getelementptr inbounds i64, ptr %28, i64 2 +// CHECK-NEXT: store i64 3, ptr %31, align 8 +// CHECK-NEXT: %32 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %28, 0 +// CHECK-NEXT: %33 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %32, i64 3, 1 +// CHECK-NEXT: %34 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %33, i64 3, 2 +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @16, i64 5 }, ptr %20, align 8 // CHECK-NEXT: store ptr %22, ptr %21, align 8 -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.Slice" %32, ptr %25, align 8 -// CHECK-NEXT: %33 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 0 -// CHECK-NEXT: %34 = load %"{{.*}}/runtime/internal/runtime.String", ptr %33, align 8 -// CHECK-NEXT: %35 = extractvalue %"{{.*}}/runtime/internal/runtime.String" %34, 1 -// CHECK-NEXT: %36 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 1 -// CHECK-NEXT: %37 = load ptr, ptr %36, align 8 -// CHECK-NEXT: %38 = call i64 @"{{.*}}/runtime/internal/runtime.ChanLen"(ptr %37) -// CHECK-NEXT: %39 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 2 -// CHECK-NEXT: %40 = load ptr, ptr %39, align 8 -// CHECK-NEXT: %41 = call i64 @"{{.*}}/runtime/internal/runtime.MapLen"(ptr %40) -// CHECK-NEXT: %42 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 3 -// CHECK-NEXT: %43 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %42, align 8 -// CHECK-NEXT: %44 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %43, 1 -// CHECK-NEXT: %45 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 1 -// CHECK-NEXT: %46 = load ptr, ptr %45, align 8 -// CHECK-NEXT: %47 = call i64 @"{{.*}}/runtime/internal/runtime.ChanCap"(ptr %46) -// CHECK-NEXT: %48 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 3 -// CHECK-NEXT: %49 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %48, align 8 -// CHECK-NEXT: %50 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %49, 2 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %35) +// CHECK-NEXT: store ptr %24, ptr %23, align 8 +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.Slice" %34, ptr %27, align 8 +// CHECK-NEXT: %35 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 0 +// CHECK-NEXT: %36 = load %"{{.*}}/runtime/internal/runtime.String", ptr %35, align 8 +// CHECK-NEXT: %37 = extractvalue %"{{.*}}/runtime/internal/runtime.String" %36, 1 +// CHECK-NEXT: %38 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 1 +// CHECK-NEXT: %39 = load ptr, ptr %38, align 8 +// CHECK-NEXT: %40 = call i64 @"{{.*}}/runtime/internal/runtime.ChanLen"(ptr %39) +// CHECK-NEXT: %41 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 2 +// CHECK-NEXT: %42 = load ptr, ptr %41, align 8 +// CHECK-NEXT: %43 = call i64 @"{{.*}}/runtime/internal/runtime.MapLen"(ptr %42) +// CHECK-NEXT: %44 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 3 +// CHECK-NEXT: %45 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %44, align 8 +// CHECK-NEXT: %46 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %45, 1 +// CHECK-NEXT: %47 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 1 +// CHECK-NEXT: %48 = load ptr, ptr %47, align 8 +// CHECK-NEXT: %49 = call i64 @"{{.*}}/runtime/internal/runtime.ChanCap"(ptr %48) +// CHECK-NEXT: %50 = getelementptr inbounds %main.data, ptr %19, i32 0, i32 3 +// CHECK-NEXT: %51 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %50, align 8 +// CHECK-NEXT: %52 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %51, 2 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %37) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %38) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %40) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %41) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %43) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %44) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %46) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %47) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %49) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %50) +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %52) // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: ret void // CHECK-NEXT: } diff --git a/cl/_testrt/tpunsafe/in.go b/cl/_testrt/tpunsafe/in.go index ae7cf159cd..a19bd33d6e 100644 --- a/cl/_testrt/tpunsafe/in.go +++ b/cl/_testrt/tpunsafe/in.go @@ -18,12 +18,10 @@ type M[T any] struct { // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 12, align 1 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 12, i1 false) -// CHECK-NEXT: call void @"main.(*M[bool]).check"(ptr %.stack, i64 1, i64 8, i64 1) -// CHECK-NEXT: %.stack1 = alloca i8, i64 32, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 32, i1 false) -// CHECK-NEXT: call void @"main.(*M[int64]).check"(ptr %.stack1, i64 8, i64 16, i64 8) +// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 12) +// CHECK-NEXT: call void @"main.(*M[bool]).check"(ptr %0, i64 1, i64 8, i64 1) +// CHECK-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// CHECK-NEXT: call void @"main.(*M[int64]).check"(ptr %1, i64 8, i64 16, i64 8) // CHECK-NEXT: ret void // CHECK-NEXT: } func main() { diff --git a/cl/_testrt/typalias/in.go b/cl/_testrt/typalias/in.go index 70ceebc123..f35416ed36 100644 --- a/cl/_testrt/typalias/in.go +++ b/cl/_testrt/typalias/in.go @@ -38,13 +38,12 @@ func Print(p *Foo) { // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 4 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: %0 = getelementptr inbounds { i32, i1 }, ptr %.stack, i32 0, i32 0 -// CHECK-NEXT: %1 = getelementptr inbounds { i32, i1 }, ptr %.stack, i32 0, i32 1 -// CHECK-NEXT: store i32 100, ptr %0, align 4 -// CHECK-NEXT: store i1 true, ptr %1, align 1 -// CHECK-NEXT: call void @main.Print(ptr %.stack) +// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %1 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 0 +// CHECK-NEXT: %2 = getelementptr inbounds { i32, i1 }, ptr %0, i32 0, i32 1 +// CHECK-NEXT: store i32 100, ptr %1, align 4 +// CHECK-NEXT: store i1 true, ptr %2, align 1 +// CHECK-NEXT: call void @main.Print(ptr %0) // CHECK-NEXT: ret void // CHECK-NEXT: } func main() { diff --git a/cl/_testrt/vamethod/in.go b/cl/_testrt/vamethod/in.go index 21ddc17cf4..a7ef707b26 100644 --- a/cl/_testrt/vamethod/in.go +++ b/cl/_testrt/vamethod/in.go @@ -56,67 +56,66 @@ type IFmt interface { // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: -// CHECK-NEXT: %.stack = alloca i8, i64 8, align 8 -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// CHECK-NEXT: call void @"main.(*CFmt).SetFormat"(ptr %.stack, ptr @0) -// CHECK-NEXT: %0 = getelementptr inbounds %main.CFmt, ptr %.stack, i32 0, i32 0 -// CHECK-NEXT: %1 = load ptr, ptr %0, align 8 -// CHECK-NEXT: %2 = call i32 (ptr, ...) @printf(ptr %1, ptr @1, i64 100) -// CHECK-NEXT: call void @"main.(*CFmt).SetFormat"(ptr %.stack, ptr @2) -// CHECK-NEXT: %3 = getelementptr inbounds %main.CFmt, ptr %.stack, i32 0, i32 0 -// CHECK-NEXT: %4 = load ptr, ptr %3, align 8 -// CHECK-NEXT: %5 = call i32 (ptr, ...) @printf(ptr %4, i64 200, ptr @3) -// CHECK-NEXT: %6 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// CHECK-NEXT: %7 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @"*_llgo_main.CFmt", ptr undef }, ptr %6, 1 -// CHECK-NEXT: %8 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %7, 0 -// CHECK-NEXT: %9 = call i1 @"{{.*}}/runtime/internal/runtime.Implements"(ptr @_llgo_main.IFmt, ptr %8) -// CHECK-NEXT: br i1 %9, label %_llgo_3, label %_llgo_4 +// CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: call void @"main.(*CFmt).SetFormat"(ptr %0, ptr @0) +// CHECK-NEXT: %1 = getelementptr inbounds %main.CFmt, ptr %0, i32 0, i32 0 +// CHECK-NEXT: %2 = load ptr, ptr %1, align 8 +// CHECK-NEXT: %3 = call i32 (ptr, ...) @printf(ptr %2, ptr @1, i64 100) +// CHECK-NEXT: call void @"main.(*CFmt).SetFormat"(ptr %0, ptr @2) +// CHECK-NEXT: %4 = getelementptr inbounds %main.CFmt, ptr %0, i32 0, i32 0 +// CHECK-NEXT: %5 = load ptr, ptr %4, align 8 +// CHECK-NEXT: %6 = call i32 (ptr, ...) @printf(ptr %5, i64 200, ptr @3) +// CHECK-NEXT: %7 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// CHECK-NEXT: %8 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @"*_llgo_main.CFmt", ptr undef }, ptr %7, 1 +// CHECK-NEXT: %9 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %8, 0 +// CHECK-NEXT: %10 = call i1 @"{{.*}}/runtime/internal/runtime.Implements"(ptr @_llgo_main.IFmt, ptr %9) +// CHECK-NEXT: br i1 %10, label %_llgo_3, label %_llgo_4 // CHECK-EMPTY: // CHECK-NEXT: _llgo_1: ; preds = %_llgo_5 -// CHECK-NEXT: %10 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @20, i64 5 }, ptr %10, align 8 -// CHECK-NEXT: %11 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %10, 1 -// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %11) +// CHECK-NEXT: %11 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// CHECK-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @20, i64 5 }, ptr %11, align 8 +// CHECK-NEXT: %12 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %11, 1 +// CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %12) // CHECK-NEXT: unreachable // CHECK-EMPTY: // CHECK-NEXT: _llgo_2: ; preds = %_llgo_5 -// CHECK-NEXT: %12 = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %36) -// CHECK-NEXT: %13 = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %36, 0 -// CHECK-NEXT: %14 = getelementptr ptr, ptr %13, i64 4 -// CHECK-NEXT: %15 = load ptr, ptr %14, align 8 -// CHECK-NEXT: %16 = insertvalue { ptr, ptr } undef, ptr %15, 0 -// CHECK-NEXT: %17 = insertvalue { ptr, ptr } %16, ptr %12, 1 -// CHECK-NEXT: %18 = extractvalue { ptr, ptr } %17, 1 -// CHECK-NEXT: %19 = extractvalue { ptr, ptr } %17, 0 -// CHECK-NEXT: call void %19(ptr %18, ptr @18) -// CHECK-NEXT: %20 = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %36) -// CHECK-NEXT: %21 = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %36, 0 -// CHECK-NEXT: %22 = getelementptr ptr, ptr %21, i64 3 -// CHECK-NEXT: %23 = load ptr, ptr %22, align 8 -// CHECK-NEXT: %24 = insertvalue { ptr, ptr } undef, ptr %23, 0 -// CHECK-NEXT: %25 = insertvalue { ptr, ptr } %24, ptr %20, 1 -// CHECK-NEXT: %26 = extractvalue { ptr, ptr } %25, 1 -// CHECK-NEXT: %27 = extractvalue { ptr, ptr } %25, 0 -// CHECK-NEXT: %28 = call i32 (ptr, ...) %27(ptr %26, ptr @19, i64 100, i64 200) +// CHECK-NEXT: %13 = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %37) +// CHECK-NEXT: %14 = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %37, 0 +// CHECK-NEXT: %15 = getelementptr ptr, ptr %14, i64 4 +// CHECK-NEXT: %16 = load ptr, ptr %15, align 8 +// CHECK-NEXT: %17 = insertvalue { ptr, ptr } undef, ptr %16, 0 +// CHECK-NEXT: %18 = insertvalue { ptr, ptr } %17, ptr %13, 1 +// CHECK-NEXT: %19 = extractvalue { ptr, ptr } %18, 1 +// CHECK-NEXT: %20 = extractvalue { ptr, ptr } %18, 0 +// CHECK-NEXT: call void %20(ptr %19, ptr @18) +// CHECK-NEXT: %21 = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %37) +// CHECK-NEXT: %22 = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %37, 0 +// CHECK-NEXT: %23 = getelementptr ptr, ptr %22, i64 3 +// CHECK-NEXT: %24 = load ptr, ptr %23, align 8 +// CHECK-NEXT: %25 = insertvalue { ptr, ptr } undef, ptr %24, 0 +// CHECK-NEXT: %26 = insertvalue { ptr, ptr } %25, ptr %21, 1 +// CHECK-NEXT: %27 = extractvalue { ptr, ptr } %26, 1 +// CHECK-NEXT: %28 = extractvalue { ptr, ptr } %26, 0 +// CHECK-NEXT: %29 = call i32 (ptr, ...) %28(ptr %27, ptr @19, i64 100, i64 200) // CHECK-NEXT: ret void // CHECK-EMPTY: // CHECK-NEXT: _llgo_3: ; preds = %_llgo_0 -// CHECK-NEXT: %29 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %7, 1 -// CHECK-NEXT: %30 = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface$a85zs5wWQQoPIERm_en8plssh4spdIeeXZPC-E0TDh0", ptr %8) -// CHECK-NEXT: %31 = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr %30, 0 -// CHECK-NEXT: %32 = insertvalue %"{{.*}}/runtime/internal/runtime.iface" %31, ptr %29, 1 -// CHECK-NEXT: %33 = insertvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } undef, %"{{.*}}/runtime/internal/runtime.iface" %32, 0 -// CHECK-NEXT: %34 = insertvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %33, i1 true, 1 +// CHECK-NEXT: %30 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %8, 1 +// CHECK-NEXT: %31 = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface$a85zs5wWQQoPIERm_en8plssh4spdIeeXZPC-E0TDh0", ptr %9) +// CHECK-NEXT: %32 = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr %31, 0 +// CHECK-NEXT: %33 = insertvalue %"{{.*}}/runtime/internal/runtime.iface" %32, ptr %30, 1 +// CHECK-NEXT: %34 = insertvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } undef, %"{{.*}}/runtime/internal/runtime.iface" %33, 0 +// CHECK-NEXT: %35 = insertvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %34, i1 true, 1 // CHECK-NEXT: br label %_llgo_5 // CHECK-EMPTY: // CHECK-NEXT: _llgo_4: ; preds = %_llgo_0 // CHECK-NEXT: br label %_llgo_5 // CHECK-EMPTY: // CHECK-NEXT: _llgo_5: ; preds = %_llgo_4, %_llgo_3 -// CHECK-NEXT: %35 = phi { %"{{.*}}/runtime/internal/runtime.iface", i1 } [ %34, %_llgo_3 ], [ zeroinitializer, %_llgo_4 ] -// CHECK-NEXT: %36 = extractvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %35, 0 -// CHECK-NEXT: %37 = extractvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %35, 1 -// CHECK-NEXT: br i1 %37, label %_llgo_2, label %_llgo_1 +// CHECK-NEXT: %36 = phi { %"{{.*}}/runtime/internal/runtime.iface", i1 } [ %35, %_llgo_3 ], [ zeroinitializer, %_llgo_4 ] +// CHECK-NEXT: %37 = extractvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %36, 0 +// CHECK-NEXT: %38 = extractvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %36, 1 +// CHECK-NEXT: br i1 %38, label %_llgo_2, label %_llgo_1 // CHECK-NEXT: } func main() { cfmt := &CFmt{} diff --git a/internal/build/build.go b/internal/build/build.go index 1710d05baf..7fedb8c70e 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -189,6 +189,8 @@ type Config struct { // DisableBoundsChecks disables index, slice, and slice-to-array conversion // bounds checks while retaining required integer conversions and nil checks. DisableBoundsChecks bool + // DisableEscapeAnalysis skips the heap-to-stack transform. + DisableEscapeAnalysis bool // PthreadStackSize sets a custom stack size, in bytes, for pthread-backed // goroutines. A zero value keeps the platform pthread default. @@ -1834,8 +1836,10 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { } ctx.cTransformer.SetSkipFuncs(cabiSkipFuncsForPlan9Asm(ctx, pkgPath, ret.Module())) - if err := llescape.TransformModule(ret.Module()); err != nil { - return fmt.Errorf("run escape analysis for %v failed: %w", pkgPath, err) + if !ctx.buildConf.DisableEscapeAnalysis { + if err := llescape.TransformModule(ret.Module()); err != nil { + return fmt.Errorf("run escape analysis for %v failed: %w", pkgPath, err) + } } llabi.LowerLargeAggregates(ctx.prog.TargetData(), ret.Module()) ctx.cTransformer.TransformModule(ret.Path(), ret.Module()) diff --git a/internal/llgen/llgenf.go b/internal/llgen/llgenf.go index 2629328a05..cd2cd05211 100644 --- a/internal/llgen/llgenf.go +++ b/internal/llgen/llgenf.go @@ -28,7 +28,7 @@ import ( ) func GenFrom(fileOrPkg string) string { - pkg, err := genFrom(fileOrPkg, 0) + pkg, err := genFrom(fileOrPkg, 0, true) check(err) out := pkg.LPkg.String() // Release the compile's LLVM context: golden suites call GenFrom for @@ -38,11 +38,12 @@ func GenFrom(fileOrPkg string) string { return out } -func genFrom(pkgPath string, abiMode build.AbiMode) (build.Package, error) { +func genFrom(pkgPath string, abiMode build.AbiMode, disableEscapeAnalysis bool) (build.Package, error) { conf := &build.Config{ - Mode: build.ModeGen, - AbiMode: abiMode, - GenLL: true, + Mode: build.ModeGen, + AbiMode: abiMode, + GenLL: true, + DisableEscapeAnalysis: disableEscapeAnalysis, } if err := applyGoBuildFlagsFile(conf, filepath.Join(pkgPath, "flags.txt")); err != nil { return nil, err @@ -91,7 +92,7 @@ func SmartDoFile(pkgPath string) { } func SmartDoFileEx(pkgPath string, abiMode build.AbiMode) { - pkg, err := genFrom(pkgPath, abiMode) + pkg, err := genFrom(pkgPath, abiMode, false) check(err) const autgenFile = "llgo_autogen.ll" diff --git a/internal/llgen/llgenf_test.go b/internal/llgen/llgenf_test.go index 006e0c5e1e..6740b551d4 100644 --- a/internal/llgen/llgenf_test.go +++ b/internal/llgen/llgenf_test.go @@ -62,3 +62,10 @@ func TestApplyGoBuildFlagsFileErrorIncludesPath(t *testing.T) { t.Fatalf("applyGoBuildFlagsFile(%q) error = %v, want path", data, err) } } + +func TestGenFromDisablesEscapeAnalysis(t *testing.T) { + ir := GenFrom("../build/testdata/escape_phase1") + if !strings.Contains(ir, "runtime.AllocZ") { + t.Fatalf("GenFrom output has no pre-transform heap allocation:\n%s", ir) + } +} From 57713be321c139c4bf4528ce4215ce2e2632825c Mon Sep 17 00:00:00 2001 From: Rick Guo Date: Sat, 1 Aug 2026 17:57:46 +0800 Subject: [PATCH 08/12] test: check escape-transformed LITTEST IR --- chore/litgen/litgen.go | 3 ++ chore/litgen/rewrite.go | 3 ++ cl/_testdata/print/in.go | 23 +++++++++++++++ cl/_testgo/alias/in.go | 10 +++++++ cl/_testgo/cgobasic/cgobasic.go | 48 +++++++++++++++++++++++++++++++ cl/_testgo/cgocfiles/cgocfiles.go | 8 ++++++ cl/_testgo/cgodefer/cgodefer.go | 8 ++++++ cl/_testgo/cgomacro/cgomacro.go | 44 ++++++++++++++++++++++++++++ cl/_testgo/cgopython/cgopython.go | 8 ++++++ cl/_testgo/cursor/in.go | 10 +++++++ cl/_testgo/returnorder/in.go | 10 +++++++ cl/_testgo/tprecurfn/in.go | 10 +++++++ cl/_testgo/tptypes/in.go | 20 +++++++++++++ cl/_testlibc/atomic/in.go | 11 +++++++ cl/_testlibgo/atomic/in.go | 11 +++++++ cl/_testrt/abitype/in.go | 13 +++++++++ cl/_testrt/builtin/in.go | 9 ++++++ cl/_testrt/eface/in.go | 8 ++++++ cl/_testrt/float2any/in.go | 20 +++++++++++++ cl/_testrt/funcaddr/in.go | 10 +++++++ cl/_testrt/funcdecl/in.go | 10 +++++++ cl/_testrt/index/in.go | 10 +++++++ cl/_testrt/len/in.go | 22 ++++++++++++++ cl/_testrt/tpunsafe/in.go | 10 +++++++ cl/_testrt/typalias/in.go | 11 +++++++ cl/_testrt/vamethod/in.go | 13 +++++++++ cl/cltest/cltest.go | 14 +++++++++ internal/littest/littest.go | 30 ++++++++++++++++++- internal/littest/littest_test.go | 35 ++++++++++++++++++---- internal/llgen/llgenf.go | 10 ++++++- 30 files changed, 444 insertions(+), 8 deletions(-) diff --git a/chore/litgen/litgen.go b/chore/litgen/litgen.go index 34d51fe437..802cf2aab7 100644 --- a/chore/litgen/litgen.go +++ b/chore/litgen/litgen.go @@ -26,8 +26,11 @@ import ( "github.com/goplus/llgo/xtool/env/llvm" ) +var escapeChecks bool + func main() { llvm.SetupPath() + flag.BoolVar(&escapeChecks, "escape", false, "generate checks after escape analysis") flag.Usage = func() { fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [...]\n", filepath.Base(os.Args[0])) flag.PrintDefaults() diff --git a/chore/litgen/rewrite.go b/chore/litgen/rewrite.go index 209a1a3d7f..667791dec6 100644 --- a/chore/litgen/rewrite.go +++ b/chore/litgen/rewrite.go @@ -128,6 +128,9 @@ func genIR(target string) (ret string, err error) { } } }() + if escapeChecks { + return llgen.GenFromWithEscape(target), nil + } return llgen.GenFrom(target), nil } diff --git a/cl/_testdata/print/in.go b/cl/_testdata/print/in.go index b934f989bc..5864c7a2f7 100644 --- a/cl/_testdata/print/in.go +++ b/cl/_testdata/print/in.go @@ -58,6 +58,29 @@ type stringStruct struct { // CHECK-NEXT: ret %"{{.*}}/runtime/internal/runtime.Slice" %13 // CHECK-NEXT: } +// ESCAPE-LABEL: define %"{{.*}}/runtime/internal/runtime.Slice" @main.bytes(%"{{.*}}/runtime/internal/runtime.String" %0){{.*}} { +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" %0, ptr %1, align 8 +// ESCAPE-NEXT: %.stack = alloca i8, i64 24, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) +// ESCAPE-NEXT: %2 = call ptr @main.stringStructOf(ptr %1) +// ESCAPE-NEXT: %3 = getelementptr inbounds %main.stringStruct, ptr %2, i32 0, i32 0 +// ESCAPE-NEXT: %4 = load ptr, ptr %3, align 8 +// ESCAPE-NEXT: %5 = getelementptr inbounds %main.slice, ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: store ptr %4, ptr %5, align 8 +// ESCAPE-NEXT: %6 = getelementptr inbounds %main.stringStruct, ptr %2, i32 0, i32 1 +// ESCAPE-NEXT: %7 = load i64, ptr %6, align 8 +// ESCAPE-NEXT: %8 = getelementptr inbounds %main.slice, ptr %.stack, i32 0, i32 1 +// ESCAPE-NEXT: store i64 %7, ptr %8, align 8 +// ESCAPE-NEXT: %9 = getelementptr inbounds %main.stringStruct, ptr %2, i32 0, i32 1 +// ESCAPE-NEXT: %10 = load i64, ptr %9, align 8 +// ESCAPE-NEXT: %11 = getelementptr inbounds %main.slice, ptr %.stack, i32 0, i32 2 +// ESCAPE-NEXT: store i64 %10, ptr %11, align 8 +// ESCAPE-NEXT: %12 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %.stack, align 8 +// ESCAPE-NEXT: ret %"{{.*}}/runtime/internal/runtime.Slice" %12 +// ESCAPE-NEXT: } + func bytes(s string) (ret []byte) { rp := (*slice)(unsafe.Pointer(&ret)) sp := stringStructOf(&s) diff --git a/cl/_testgo/alias/in.go b/cl/_testgo/alias/in.go index 20d91abba6..4b7235acd3 100644 --- a/cl/_testgo/alias/in.go +++ b/cl/_testgo/alias/in.go @@ -44,6 +44,16 @@ func (p *Point) Scale(factor float64) { p.y *= factor } +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 16, align 8 +// ESCAPE: call void @"main.(*Point).Scale"(ptr %.stack, double 2.000000e+00) +// ESCAPE: call void @"main.(*Point).Move"(ptr %.stack, double 3.000000e+00, double 4.000000e+00) +// ESCAPE: call void @"{{.*}}PrintFloat"(double %3) +// ESCAPE-NEXT: call void @"{{.*}}PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}PrintFloat"(double %5) +// ESCAPE-NEXT: call void @"{{.*}}PrintByte"(i8 10) +// ESCAPE-NEXT: ret void + // CHECK-LABEL: define void @main.main(){{.*}} { func main() { // CHECK: call ptr @"{{.*}}AllocZ"(i64 16) diff --git a/cl/_testgo/cgobasic/cgobasic.go b/cl/_testgo/cgobasic/cgobasic.go index 8017d92376..1e7aa6bd8a 100644 --- a/cl/_testgo/cgobasic/cgobasic.go +++ b/cl/_testgo/cgobasic/cgobasic.go @@ -66,6 +66,54 @@ import ( // CHECK-NEXT: ret double %4 // CHECK-NEXT: } +// ESCAPE-LABEL: define double @main._Cfunc_cos(double %0){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_cos, align 8 +// ESCAPE: %2 = load ptr, ptr %1, align 8 +// ESCAPE: %3 = call double %2(double %0) +// ESCAPE: ret double %3 + +// ESCAPE-LABEL: define [0 x i8] @main._Cfunc_free(ptr %0){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 +// ESCAPE: %2 = load ptr, ptr %1, align 8 +// ESCAPE: %3 = call [0 x i8] %2(ptr %0) +// ESCAPE: ret [0 x i8] %3 + +// ESCAPE-LABEL: define double @main._Cfunc_log(double %0){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_log, align 8 +// ESCAPE: %2 = load ptr, ptr %1, align 8 +// ESCAPE: %3 = call double %2(double %0) +// ESCAPE: ret double %3 + +// ESCAPE-LABEL: define i32 @main._Cfunc_puts(ptr %0){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_puts, align 8 +// ESCAPE: %2 = load ptr, ptr %1, align 8 +// ESCAPE: %3 = call i32 %2(ptr %0) +// ESCAPE: ret i32 %3 + +// ESCAPE-LABEL: define double @main._Cfunc_sin(double %0){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sin, align 8 +// ESCAPE: %2 = load ptr, ptr %1, align 8 +// ESCAPE: %3 = call double %2(double %0) +// ESCAPE: ret double %3 + +// ESCAPE-LABEL: define double @main._Cfunc_sqrt(double %0){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sqrt, align 8 +// ESCAPE: %2 = load ptr, ptr %1, align 8 +// ESCAPE: %3 = call double %2(double %0) +// ESCAPE: ret double %3 + // CHECK-LABEL: define void @main.init(){{.*}} { // CHECK-NEXT: _llgo_0: // CHECK-NEXT: %0 = load i1, ptr @"main.init$guard", align 1 diff --git a/cl/_testgo/cgocfiles/cgocfiles.go b/cl/_testgo/cgocfiles/cgocfiles.go index 51d85ab1e8..37ba4fc45c 100644 --- a/cl/_testgo/cgocfiles/cgocfiles.go +++ b/cl/_testgo/cgocfiles/cgocfiles.go @@ -16,6 +16,14 @@ import "fmt" // CHECK-NEXT: ret i32 %8 // CHECK-NEXT: } +// ESCAPE-LABEL: define i32 @main._Cfunc_test_structs(ptr %0, ptr %1, ptr %2, ptr %3, ptr %4){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %5 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_test_structs, align 8 +// ESCAPE: %6 = load ptr, ptr %5, align 8 +// ESCAPE: %7 = call i32 %6(ptr %0, ptr %1, ptr %2, ptr %3, ptr %4) +// ESCAPE: ret i32 %7 + // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: // CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 4) diff --git a/cl/_testgo/cgodefer/cgodefer.go b/cl/_testgo/cgodefer/cgodefer.go index 8d9bca1306..0e0a68ae63 100644 --- a/cl/_testgo/cgodefer/cgodefer.go +++ b/cl/_testgo/cgodefer/cgodefer.go @@ -15,6 +15,14 @@ import "C" // CHECK-NEXT: ret [0 x i8] %4 // CHECK-NEXT: } +// ESCAPE-LABEL: define [0 x i8] @main._Cfunc_free(ptr %0){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 +// ESCAPE: %2 = load ptr, ptr %1, align 8 +// ESCAPE: %3 = call [0 x i8] %2(ptr %0) +// ESCAPE: ret [0 x i8] %3 + // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: // CHECK-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) diff --git a/cl/_testgo/cgomacro/cgomacro.go b/cl/_testgo/cgomacro/cgomacro.go index c2c152d37d..98f89c1a8f 100644 --- a/cl/_testgo/cgomacro/cgomacro.go +++ b/cl/_testgo/cgomacro/cgomacro.go @@ -92,6 +92,50 @@ import ( // CHECK-NEXT: ret ptr %2 // CHECK-NEXT: } +// ESCAPE-LABEL: define i32 @main._Cfunc_PyObject_Print(ptr %0, ptr %1, i32 %2){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %3 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyObject_Print, align 8 +// ESCAPE: %4 = load ptr, ptr %3, align 8 +// ESCAPE: %5 = call i32 %4(ptr %0, ptr %1, i32 %2) +// ESCAPE: ret i32 %5 + +// ESCAPE-LABEL: define i32 @main._Cfunc_fputs(ptr %0, ptr %1){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_fputs, align 8 +// ESCAPE: %3 = load ptr, ptr %2, align 8 +// ESCAPE: %4 = call i32 %3(ptr %0, ptr %1) +// ESCAPE: ret i32 %4 + +// ESCAPE-LABEL: define ptr @main._Cmacro_Py_False(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_False, align 8 +// ESCAPE: %1 = load ptr, ptr %0, align 8 +// ESCAPE: ret ptr %1 + +// ESCAPE-LABEL: define ptr @main._Cmacro_Py_None(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_None, align 8 +// ESCAPE: %1 = load ptr, ptr %0, align 8 +// ESCAPE: ret ptr %1 + +// ESCAPE-LABEL: define ptr @main._Cmacro_Py_True(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_True, align 8 +// ESCAPE: %1 = load ptr, ptr %0, align 8 +// ESCAPE: ret ptr %1 + +// ESCAPE-LABEL: define ptr @main._Cmacro_stdout(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_stdout, align 8 +// ESCAPE: %1 = load ptr, ptr %0, align 8 +// ESCAPE: ret ptr %1 + // CHECK-LABEL: define void @main.init(){{.*}} { // CHECK-NEXT: _llgo_0: // CHECK-NEXT: %0 = load i1, ptr @"main.init$guard", align 1 diff --git a/cl/_testgo/cgopython/cgopython.go b/cl/_testgo/cgopython/cgopython.go index aea2a0f615..a002de627a 100644 --- a/cl/_testgo/cgopython/cgopython.go +++ b/cl/_testgo/cgopython/cgopython.go @@ -16,6 +16,14 @@ import "C" // CHECK-NEXT: ret i32 %4 // CHECK-NEXT: } +// ESCAPE-LABEL: define i32 @main._Cfunc_PyRun_SimpleString(ptr %0){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 1 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyRun_SimpleString, align 8 +// ESCAPE: %2 = load ptr, ptr %1, align 8 +// ESCAPE: %3 = call i32 %2(ptr %0) +// ESCAPE: ret i32 %3 + // CHECK-LABEL: define [0 x i8] @main._Cfunc_Py_Finalize(){{.*}} { // CHECK-NEXT: _llgo_0: // CHECK-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_Py_Finalize, align 8 diff --git a/cl/_testgo/cursor/in.go b/cl/_testgo/cursor/in.go index d0a2fc24bf..e3a70cf809 100644 --- a/cl/_testgo/cursor/in.go +++ b/cl/_testgo/cursor/in.go @@ -805,6 +805,16 @@ const ( // CHECK-NEXT: ret void // CHECK-NEXT: } +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 16, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: %0 = getelementptr inbounds %main.Cursor, ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// ESCAPE-NEXT: store ptr %1, ptr %0, align 8 +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } + // CHECK-LABEL: define i64 @main.maskOf(%"{{.*}}/runtime/internal/runtime.Slice" %0){{.*}} { // CHECK-NEXT: _llgo_0: // CHECK-NEXT: %1 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %0, 1 diff --git a/cl/_testgo/returnorder/in.go b/cl/_testgo/returnorder/in.go index de98fd2b9b..296ed56a5b 100644 --- a/cl/_testgo/returnorder/in.go +++ b/cl/_testgo/returnorder/in.go @@ -36,6 +36,16 @@ func main() { // CHECK-NEXT: %4 = insertvalue { %main.state, i64 } undef, %main.state %3, 0 // CHECK-NEXT: %5 = insertvalue { %main.state, i64 } %4, i64 %2, 1 // CHECK-NEXT: ret { %main.state, i64 } %5 +// ESCAPE-LABEL: define { %main.state, i64 } @main.returnStateAndMut(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: %0 = getelementptr inbounds %main.state, ptr %.stack, i32 0, i32 0 +// ESCAPE: store i64 1, ptr %0, align 8 +// ESCAPE: %1 = call i64 @"main.(*state).mutate"(ptr %.stack, i64 2) +// ESCAPE: %2 = load %main.state, ptr %.stack, align 8 +// ESCAPE: %3 = insertvalue { %main.state, i64 } undef, %main.state %2, 0 +// ESCAPE: %4 = insertvalue { %main.state, i64 } %3, i64 %1, 1 +// ESCAPE: ret { %main.state, i64 } %4 func returnStateAndMut() (state, int) { x := state{v: 1} return x, x.mutate(2) diff --git a/cl/_testgo/tprecurfn/in.go b/cl/_testgo/tprecurfn/in.go index de9928916d..6abde1e97f 100644 --- a/cl/_testgo/tprecurfn/in.go +++ b/cl/_testgo/tprecurfn/in.go @@ -6,6 +6,16 @@ type My[T any] struct { next *My[T] } +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 24, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) +// ESCAPE: %0 = getelementptr inbounds %"main.My[int]", ptr %.stack, i32 0, i32 1 +// ESCAPE: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// ESCAPE: store { ptr, ptr } { ptr @"__llgo_stub.main.main$1", ptr null }, ptr {{%.*}}, align 8 +// ESCAPE: store ptr %1, ptr %0, align 8 +// ESCAPE: call void {{%.*}}(ptr {{%.*}}, i64 100) +// ESCAPE: ret void + // CHECK-LABEL: define void @main.main(){{.*}} { func main() { // CHECK: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) diff --git a/cl/_testgo/tptypes/in.go b/cl/_testgo/tptypes/in.go index c6130848bd..d0e3b30115 100644 --- a/cl/_testgo/tptypes/in.go +++ b/cl/_testgo/tptypes/in.go @@ -177,6 +177,26 @@ type ( // CHECK-NEXT: ret void // CHECK-NEXT: } +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 24, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) +// ESCAPE: %16 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// ESCAPE: call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %.stack, %"{{.*}}/runtime/internal/runtime.Slice" {{%.*}}) +// ESCAPE: %.stack1 = alloca i8, i64 24, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 24, i1 false) +// ESCAPE: %22 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) +// ESCAPE: call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]string,string\]}}).Append"(ptr %.stack1, %"{{.*}}/runtime/internal/runtime.Slice" {{%.*}}) +// ESCAPE: %.stack2 = alloca i8, i64 24, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack2, i8 0, i64 24, i1 false) +// ESCAPE: %28 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// ESCAPE: call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %.stack2, %"{{.*}}/runtime/internal/runtime.Slice" {{%.*}}) +// ESCAPE: %37 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// ESCAPE: call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append2"(ptr %.stack2, %"{{.*}}/runtime/internal/runtime.Slice" {{%.*}}) +// ESCAPE: getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack, i32 0, i32 0 +// ESCAPE: getelementptr inbounds %"main.Slice{{\[\[\]string,string\]}}", ptr %.stack1, i32 0, i32 0 +// ESCAPE: getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack2, i32 0, i32 0 +// ESCAPE: ret void + func main() { println(DataInt{1}.v) println(DataString{"hello"}.v) diff --git a/cl/_testlibc/atomic/in.go b/cl/_testlibc/atomic/in.go index f1681d9c38..267a414a1a 100644 --- a/cl/_testlibc/atomic/in.go +++ b/cl/_testlibc/atomic/in.go @@ -6,6 +6,17 @@ import ( "github.com/goplus/lib/c/sync/atomic" ) +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: store atomic i64 100, ptr %.stack seq_cst, align 8 +// ESCAPE: load atomic i64, ptr %.stack seq_cst, align 8 +// ESCAPE: atomicrmw add ptr %.stack, i64 1 seq_cst, align 8 +// ESCAPE: cmpxchg ptr %.stack, i64 100, i64 102 seq_cst seq_cst, align 8 +// ESCAPE: cmpxchg ptr %.stack, i64 101, i64 102 seq_cst seq_cst, align 8 +// ESCAPE: atomicrmw sub ptr %.stack, i64 1 seq_cst, align 8 +// ESCAPE: ret void + // CHECK-LABEL: define void @main.main(){{.*}} { func main() { var v int64 diff --git a/cl/_testlibgo/atomic/in.go b/cl/_testlibgo/atomic/in.go index d97635e548..1cf2913eee 100644 --- a/cl/_testlibgo/atomic/in.go +++ b/cl/_testlibgo/atomic/in.go @@ -5,6 +5,17 @@ import ( "sync/atomic" ) +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: store atomic i64 100, ptr %.stack seq_cst, align 8 +// ESCAPE: load atomic i64, ptr %.stack seq_cst, align 8 +// ESCAPE: atomicrmw add ptr %.stack, i64 1 seq_cst, align 8 +// ESCAPE: cmpxchg ptr %.stack, i64 100, i64 102 seq_cst seq_cst, align 8 +// ESCAPE: cmpxchg ptr %.stack, i64 101, i64 102 seq_cst seq_cst, align 8 +// ESCAPE: atomicrmw add ptr %.stack, i64 -1 seq_cst, align 8 +// ESCAPE: ret void + // CHECK-LABEL: define void @main.main(){{.*}} { func main() { var v int64 diff --git a/cl/_testrt/abitype/in.go b/cl/_testrt/abitype/in.go index ced9c0c2d7..f82f9a0ca1 100644 --- a/cl/_testrt/abitype/in.go +++ b/cl/_testrt/abitype/in.go @@ -51,6 +51,19 @@ import ( // CHECK-NEXT: ret void // CHECK-NEXT: } +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 16, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 4) +// ESCAPE: store %"{{.*}}/runtime/internal/runtime.eface" {{%.*}}, ptr %.stack, align 8 +// ESCAPE: getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 +// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 1) +// ESCAPE: store %"{{.*}}/runtime/internal/runtime.eface" {{%.*}}, ptr %.stack, align 8 +// ESCAPE: getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 +// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// ESCAPE: ret void + type eface struct { typ *abi.Type data unsafe.Pointer diff --git a/cl/_testrt/builtin/in.go b/cl/_testrt/builtin/in.go index fdc7ff8da0..dff734c1be 100644 --- a/cl/_testrt/builtin/in.go +++ b/cl/_testrt/builtin/in.go @@ -37,6 +37,15 @@ const ( // CHECK-NEXT: ret double %2 // CHECK-NEXT: } +// ESCAPE-LABEL: define double @main.Float64frombits(i64 %0){{.*}} { +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: store i64 %0, ptr %.stack, align 8 +// ESCAPE-NEXT: %1 = load double, ptr %.stack, align 8 +// ESCAPE-NEXT: ret double %1 +// ESCAPE-NEXT: } + func Float64frombits(b uint64) float64 { return *(*float64)(unsafe.Pointer(&b)) } // CHECK-LABEL: define double @main.Inf(i64 %0){{.*}} { diff --git a/cl/_testrt/eface/in.go b/cl/_testrt/eface/in.go index 68b29347b3..1d3ad19c7b 100644 --- a/cl/_testrt/eface/in.go +++ b/cl/_testrt/eface/in.go @@ -26,6 +26,14 @@ func (t *T) Invoke() { // CHECK-NEXT: call void @main.dumpTyp(ptr %3, %"{{.*}}/runtime/internal/runtime.String" zeroinitializer) // CHECK-NEXT: ret void // CHECK-NEXT: } +// ESCAPE-LABEL: define void @main.dump(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 16, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 +// ESCAPE: %1 = getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 +// ESCAPE: %2 = load ptr, ptr %1, align 8 +// ESCAPE: call void @main.dumpTyp(ptr %2, %"{{.*}}/runtime/internal/runtime.String" zeroinitializer) +// ESCAPE: ret void func dump(v any) { e := (*eface)(unsafe.Pointer(&v)) dumpTyp(e._type, "") diff --git a/cl/_testrt/float2any/in.go b/cl/_testrt/float2any/in.go index ee0e1cf095..81763b91e5 100644 --- a/cl/_testrt/float2any/in.go +++ b/cl/_testrt/float2any/in.go @@ -18,6 +18,15 @@ type u64parts struct { hi uint32 } +// ESCAPE-LABEL: define void @main.check32(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 16, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 +// ESCAPE: load %"{{.*}}/runtime/internal/runtime.eface", ptr %.stack, align 8 +// ESCAPE: icmp eq ptr {{.*}}, @_llgo_float32 +// ESCAPE: load i32, ptr +// ESCAPE: icmp ne i32 {{.*}}, 1078530011 + // CHECK-LABEL: define void @main.check32(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { // CHECK: icmp eq ptr {{.*}}, @_llgo_float32 // CHECK: load i32, ptr @@ -34,6 +43,17 @@ func check32(v any) { } } +// ESCAPE-LABEL: define void @main.check64(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 16, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 +// ESCAPE: load %"{{.*}}/runtime/internal/runtime.eface", ptr %.stack, align 8 +// ESCAPE: icmp eq ptr {{.*}}, @_llgo_float64 +// ESCAPE: load i32, ptr +// ESCAPE: icmp ne i32 {{.*}}, 1405670641 +// ESCAPE: load i32, ptr +// ESCAPE: icmp ne i32 {{.*}}, 1074340347 + // CHECK-LABEL: define void @main.check64(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { // CHECK: icmp eq ptr {{.*}}, @_llgo_float64 // CHECK: load i32, ptr diff --git a/cl/_testrt/funcaddr/in.go b/cl/_testrt/funcaddr/in.go index 6457b31b93..3a36f13734 100644 --- a/cl/_testrt/funcaddr/in.go +++ b/cl/_testrt/funcaddr/in.go @@ -41,6 +41,16 @@ func add(a, b int) int { // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: ret void // CHECK-NEXT: } +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: store ptr @main.add, ptr %.stack, align 8 +// ESCAPE: %.stack1 = alloca i8, i64 8, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 8, i1 false) +// ESCAPE: store ptr @"main.main$1", ptr %.stack1, align 8 +// ESCAPE: load ptr, ptr %.stack, align 8 +// ESCAPE: load ptr, ptr %.stack1, align 8 +// ESCAPE: ret void func main() { var fn Add = add // CHECK-LABEL: define i64 @"main.main$1"(i64 %0, i64 %1){{.*}} { diff --git a/cl/_testrt/funcdecl/in.go b/cl/_testrt/funcdecl/in.go index 8efd3f4635..86a9dbe48e 100644 --- a/cl/_testrt/funcdecl/in.go +++ b/cl/_testrt/funcdecl/in.go @@ -82,6 +82,16 @@ func check(fn func()) { // CHECK-NEXT: ret ptr %5 // CHECK-NEXT: } +// ESCAPE-LABEL: define ptr @main.closurePtr(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 16, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 +// ESCAPE: %1 = getelementptr inbounds %main.rtype, ptr %.stack, i32 0, i32 1 +// ESCAPE: %2 = load ptr, ptr %1, align 8 +// ESCAPE: %3 = getelementptr inbounds { ptr, ptr }, ptr %2, i32 0, i32 0 +// ESCAPE: %4 = load ptr, ptr %3, align 8 +// ESCAPE: ret ptr %4 + func closurePtr(a any) unsafe.Pointer { return (*rtype)(unsafe.Pointer(&a)).ptr.fn } diff --git a/cl/_testrt/index/in.go b/cl/_testrt/index/in.go index 8f19ac4768..915b933d7f 100644 --- a/cl/_testrt/index/in.go +++ b/cl/_testrt/index/in.go @@ -153,6 +153,16 @@ type S []int // CHECK-NEXT: ret void // CHECK-NEXT: } +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 16, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE: getelementptr inbounds i64, ptr %.stack, i64 0 +// ESCAPE: getelementptr inbounds i64, ptr %.stack, i64 1 +// ESCAPE: load i64, ptr {{%.*}}, align 8 +// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// ESCAPE: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange" +// ESCAPE: ret void + func main() { a := [...]point{{1, 2}, {3, 4}, {5, 6}}[2] println(a.x, a.y) diff --git a/cl/_testrt/len/in.go b/cl/_testrt/len/in.go index 3bd00461b1..53e9ec9ee0 100644 --- a/cl/_testrt/len/in.go +++ b/cl/_testrt/len/in.go @@ -98,6 +98,28 @@ type data struct { // CHECK-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) // CHECK-NEXT: ret void // CHECK-NEXT: } +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 56, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 56, i1 false) +// ESCAPE: getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 0 +// ESCAPE: getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 1 +// ESCAPE: getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 2 +// ESCAPE: getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 3 +// ESCAPE: %.stack1 = alloca i8, i64 56, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 56, i1 false) +// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.NewChan"(i64 8, i64 2) +// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.MakeMap" +// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 8) +// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// ESCAPE: store %"{{.*}}/runtime/internal/runtime.String" {{.*}}, ptr {{%.*}}, align 8 +// ESCAPE: store ptr {{%.*}}, ptr {{%.*}}, align 8 +// ESCAPE: store ptr {{%.*}}, ptr {{%.*}}, align 8 +// ESCAPE: store %"{{.*}}/runtime/internal/runtime.Slice" {{%.*}}, ptr {{%.*}}, align 8 +// ESCAPE: getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 0 +// ESCAPE: getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 1 +// ESCAPE: getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 2 +// ESCAPE: getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 3 +// ESCAPE: ret void func main() { d := &data{} println(len(d.s), len(d.c), len(d.m), len(d.a), cap(d.c), cap(d.a)) diff --git a/cl/_testrt/tpunsafe/in.go b/cl/_testrt/tpunsafe/in.go index a19bd33d6e..7a5a015410 100644 --- a/cl/_testrt/tpunsafe/in.go +++ b/cl/_testrt/tpunsafe/in.go @@ -24,6 +24,16 @@ type M[T any] struct { // CHECK-NEXT: call void @"main.(*M[int64]).check"(ptr %1, i64 8, i64 16, i64 8) // CHECK-NEXT: ret void // CHECK-NEXT: } +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 12, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 12, i1 false) +// ESCAPE-NEXT: call void @"main.(*M[bool]).check"(ptr %.stack, i64 1, i64 8, i64 1) +// ESCAPE-NEXT: %.stack1 = alloca i8, i64 32, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 32, i1 false) +// ESCAPE-NEXT: call void @"main.(*M[int64]).check"(ptr %.stack1, i64 8, i64 16, i64 8) +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } func main() { m1 := M[bool]{} m1.check(1, 8, 1) diff --git a/cl/_testrt/typalias/in.go b/cl/_testrt/typalias/in.go index f35416ed36..0373fa10c5 100644 --- a/cl/_testrt/typalias/in.go +++ b/cl/_testrt/typalias/in.go @@ -46,6 +46,17 @@ func Print(p *Foo) { // CHECK-NEXT: call void @main.Print(ptr %0) // CHECK-NEXT: ret void // CHECK-NEXT: } +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 4 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %0 = getelementptr inbounds { i32, i1 }, ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: %1 = getelementptr inbounds { i32, i1 }, ptr %.stack, i32 0, i32 1 +// ESCAPE-NEXT: store i32 100, ptr %0, align 4 +// ESCAPE-NEXT: store i1 true, ptr %1, align 1 +// ESCAPE-NEXT: call void @main.Print(ptr %.stack) +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } func main() { foo := &Foo{100, true} Print(foo) diff --git a/cl/_testrt/vamethod/in.go b/cl/_testrt/vamethod/in.go index a7ef707b26..f49b89bca4 100644 --- a/cl/_testrt/vamethod/in.go +++ b/cl/_testrt/vamethod/in.go @@ -117,6 +117,19 @@ type IFmt interface { // CHECK-NEXT: %38 = extractvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %36, 1 // CHECK-NEXT: br i1 %38, label %_llgo_2, label %_llgo_1 // CHECK-NEXT: } +// ESCAPE-LABEL: define void @main.main(){{.*}} { +// ESCAPE: %.stack = alloca i8, i64 8, align 8 +// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE: call void @"main.(*CFmt).SetFormat"(ptr %.stack, ptr @0) +// ESCAPE: getelementptr inbounds %main.CFmt, ptr %.stack, i32 0, i32 0 +// ESCAPE: call i32 (ptr, ...) @printf +// ESCAPE: call void @"main.(*CFmt).SetFormat"(ptr %.stack, ptr @2) +// ESCAPE: getelementptr inbounds %main.CFmt, ptr %.stack, i32 0, i32 0 +// ESCAPE: call i32 (ptr, ...) @printf +// ESCAPE: %6 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// ESCAPE: insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @"*_llgo_main.CFmt", ptr undef }, ptr %6, 1 +// ESCAPE: call i1 @"{{.*}}/runtime/internal/runtime.Implements" +// ESCAPE: ret void func main() { cfmt := &CFmt{} cfmt.SetFormat(c.Str("%s (%d)\n")) diff --git a/cl/cltest/cltest.go b/cl/cltest/cltest.go index 4a1fc60bef..849e6992e7 100644 --- a/cl/cltest/cltest.go +++ b/cl/cltest/cltest.go @@ -261,6 +261,7 @@ func testFrom(t *testing.T, pkgDir, sel string) { _ = os.WriteFile(pkgDir+"/result.txt", []byte(v), 0644) t.Fatal(err) } + checkEscapeIR(t, pkgDir, spec) return } if test.Diff(t, pkgDir+"/result.txt", []byte(v), []byte(spec.Text)) { @@ -351,6 +352,19 @@ func testRunAndTestFrom(t *testing.T, pkgDir, relPkg, sel string, opts runOption _ = os.WriteFile(filepath.Join(pkgDir, "result.txt"), []byte(*capturedIR), 0644) t.Fatal(err) } + checkEscapeIR(t, pkgDir, irSpec) +} + +func checkEscapeIR(t *testing.T, pkgDir string, spec littest.Spec) { + t.Helper() + var ir string + withFuncInfoDisabled(func() { + ir = llgen.GenFromWithEscape(pkgDir) + }) + if err := littest.Check(spec, ir, "ESCAPE"); err != nil { + _ = os.WriteFile(filepath.Join(pkgDir, "escape-result.txt"), []byte(ir), 0644) + t.Fatal(err) + } } func assertExpectedMeta(t *testing.T, pkgDir, relPkg string, capturedMeta *string) { diff --git a/internal/littest/littest.go b/internal/littest/littest.go index 0147603631..989e3d686b 100644 --- a/internal/littest/littest.go +++ b/internal/littest/littest.go @@ -62,13 +62,41 @@ func LoadSpec(pkgDir string) (Spec, error) { return Spec{Path: path, Text: string(data), Mode: ModeLiteral}, nil } -func Check(spec Spec, actual string) error { +func Check(spec Spec, actual string, prefixes ...string) error { switch spec.Mode { case ModeSkip: return nil case ModeFileCheck: + if len(prefixes) != 0 { + data, err := os.ReadFile(spec.Path) + if err != nil { + return err + } + lines := strings.Split(string(data), "\n") + used := make([]string, 0, len(prefixes)) + for _, prefix := range prefixes { + for _, line := range lines { + line = strings.TrimSpace(line) + if !strings.HasPrefix(line, "//") { + continue + } + directive := strings.TrimSpace(strings.TrimPrefix(line, "//")) + if strings.HasPrefix(directive, prefix+":") || strings.HasPrefix(directive, prefix+"-") { + used = append(used, prefix) + break + } + } + } + if len(used) == 0 { + return nil + } + return filecheck.MatchWithPrefixes(spec.Path, actual, used...) + } return filecheck.Match(spec.Path, actual) case ModeLiteral: + if len(prefixes) != 0 { + return nil + } if actual != spec.Text { return fmt.Errorf("%s: literal LLVM IR mismatch", spec.Path) } diff --git a/internal/littest/littest_test.go b/internal/littest/littest_test.go index d02d899375..4042cc8988 100644 --- a/internal/littest/littest_test.go +++ b/internal/littest/littest_test.go @@ -195,16 +195,22 @@ func TestHasMarker(t *testing.T) { } func TestCheck(t *testing.T) { - checkPath := filepath.Join(t.TempDir(), "check.go") + dir := t.TempDir() + checkPath := filepath.Join(dir, "check.go") if err := os.WriteFile(checkPath, []byte("// CHECK: ok\n"), 0644); err != nil { t.Fatal(err) } + escapeCheckPath := filepath.Join(dir, "escape.go") + if err := os.WriteFile(escapeCheckPath, []byte("// ESCAPE: escaped\n"), 0644); err != nil { + t.Fatal(err) + } cases := []struct { - name string - spec Spec - text string - want string + name string + spec Spec + text string + prefixes []string + want string }{ { name: "skip", @@ -226,6 +232,23 @@ func TestCheck(t *testing.T) { spec: Spec{Path: checkPath, Mode: ModeFileCheck}, text: "ok\n", }, + { + name: "filecheck prefix match", + spec: Spec{Path: escapeCheckPath, Mode: ModeFileCheck}, + text: "escaped\n", + prefixes: []string{"ESCAPE"}, + }, + { + name: "filecheck unused prefix", + spec: Spec{Path: checkPath, Mode: ModeFileCheck}, + prefixes: []string{"ESCAPE"}, + }, + { + name: "literal ignores prefix", + spec: Spec{Path: "literal", Text: "ok", Mode: ModeLiteral}, + text: "escaped", + prefixes: []string{"ESCAPE"}, + }, { name: "invalid mode", spec: Spec{Path: "bad", Mode: Mode(99)}, @@ -234,7 +257,7 @@ func TestCheck(t *testing.T) { } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - err := Check(tc.spec, tc.text) + err := Check(tc.spec, tc.text, tc.prefixes...) if tc.want == "" { if err != nil { t.Fatal(err) diff --git a/internal/llgen/llgenf.go b/internal/llgen/llgenf.go index cd2cd05211..dcb0402859 100644 --- a/internal/llgen/llgenf.go +++ b/internal/llgen/llgenf.go @@ -28,7 +28,15 @@ import ( ) func GenFrom(fileOrPkg string) string { - pkg, err := genFrom(fileOrPkg, 0, true) + return genStringFrom(fileOrPkg, true) +} + +func GenFromWithEscape(fileOrPkg string) string { + return genStringFrom(fileOrPkg, false) +} + +func genStringFrom(fileOrPkg string, disableEscapeAnalysis bool) string { + pkg, err := genFrom(fileOrPkg, 0, disableEscapeAnalysis) check(err) out := pkg.LPkg.String() // Release the compile's LLVM context: golden suites call GenFrom for From 977cc825ef2a43e0afbd8329859085b263a21836 Mon Sep 17 00:00:00 2001 From: Rick Guo Date: Sat, 1 Aug 2026 18:29:09 +0800 Subject: [PATCH 09/12] test: show full escape-transformed IR --- cl/_testgo/alias/in.go | 27 ++++-- cl/_testgo/cgobasic/cgobasic.go | 84 ++++++++++-------- cl/_testgo/cgocfiles/cgocfiles.go | 14 +-- cl/_testgo/cgodefer/cgodefer.go | 14 +-- cl/_testgo/cgomacro/cgomacro.go | 76 +++++++++------- cl/_testgo/cgopython/cgopython.go | 14 +-- cl/_testgo/returnorder/in.go | 20 +++-- cl/_testgo/tprecurfn/in.go | 25 ++++-- cl/_testgo/tptypes/in.go | 143 ++++++++++++++++++++++++++---- cl/_testlibc/atomic/in.go | 33 +++++-- cl/_testlibgo/atomic/in.go | 64 +++++++++++-- cl/_testrt/abitype/in.go | 54 ++++++++--- cl/_testrt/eface/in.go | 16 ++-- cl/_testrt/float2any/in.go | 133 +++++++++++++++++++++++---- cl/_testrt/funcaddr/in.go | 32 +++++-- cl/_testrt/funcdecl/in.go | 18 ++-- cl/_testrt/index/in.go | 135 ++++++++++++++++++++++++++-- cl/_testrt/len/in.go | 112 ++++++++++++++++++----- cl/_testrt/vamethod/in.go | 75 +++++++++++++--- 19 files changed, 850 insertions(+), 239 deletions(-) diff --git a/cl/_testgo/alias/in.go b/cl/_testgo/alias/in.go index 4b7235acd3..6dbe039452 100644 --- a/cl/_testgo/alias/in.go +++ b/cl/_testgo/alias/in.go @@ -45,14 +45,25 @@ func (p *Point) Scale(factor float64) { } // ESCAPE-LABEL: define void @main.main(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 16, align 8 -// ESCAPE: call void @"main.(*Point).Scale"(ptr %.stack, double 2.000000e+00) -// ESCAPE: call void @"main.(*Point).Move"(ptr %.stack, double 3.000000e+00, double 4.000000e+00) -// ESCAPE: call void @"{{.*}}PrintFloat"(double %3) -// ESCAPE-NEXT: call void @"{{.*}}PrintByte"(i8 32) -// ESCAPE-NEXT: call void @"{{.*}}PrintFloat"(double %5) -// ESCAPE-NEXT: call void @"{{.*}}PrintByte"(i8 10) -// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 16, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: %0 = getelementptr inbounds %main.Point, ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: %1 = getelementptr inbounds %main.Point, ptr %.stack, i32 0, i32 1 +// ESCAPE-NEXT: store double 1.000000e+00, ptr %0, align 8 +// ESCAPE-NEXT: store double 2.000000e+00, ptr %1, align 8 +// ESCAPE-NEXT: call void @"main.(*Point).Scale"(ptr %.stack, double 2.000000e+00) +// ESCAPE-NEXT: call void @"main.(*Point).Move"(ptr %.stack, double 3.000000e+00, double 4.000000e+00) +// ESCAPE-NEXT: %2 = getelementptr inbounds %main.Point, ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: %3 = load double, ptr %2, align 8 +// ESCAPE-NEXT: %4 = getelementptr inbounds %main.Point, ptr %.stack, i32 0, i32 1 +// ESCAPE-NEXT: %5 = load double, ptr %4, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintFloat"(double %3) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintFloat"(double %5) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } // CHECK-LABEL: define void @main.main(){{.*}} { func main() { diff --git a/cl/_testgo/cgobasic/cgobasic.go b/cl/_testgo/cgobasic/cgobasic.go index 1e7aa6bd8a..d6fdace353 100644 --- a/cl/_testgo/cgobasic/cgobasic.go +++ b/cl/_testgo/cgobasic/cgobasic.go @@ -67,52 +67,64 @@ import ( // CHECK-NEXT: } // ESCAPE-LABEL: define double @main._Cfunc_cos(double %0){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_cos, align 8 -// ESCAPE: %2 = load ptr, ptr %1, align 8 -// ESCAPE: %3 = call double %2(double %0) -// ESCAPE: ret double %3 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_cos, align 8 +// ESCAPE-NEXT: %2 = load ptr, ptr %1, align 8 +// ESCAPE-NEXT: %3 = call double %2(double %0) +// ESCAPE-NEXT: ret double %3 +// ESCAPE-NEXT: } // ESCAPE-LABEL: define [0 x i8] @main._Cfunc_free(ptr %0){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 -// ESCAPE: %2 = load ptr, ptr %1, align 8 -// ESCAPE: %3 = call [0 x i8] %2(ptr %0) -// ESCAPE: ret [0 x i8] %3 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 +// ESCAPE-NEXT: %2 = load ptr, ptr %1, align 8 +// ESCAPE-NEXT: %3 = call [0 x i8] %2(ptr %0) +// ESCAPE-NEXT: ret [0 x i8] %3 +// ESCAPE-NEXT: } // ESCAPE-LABEL: define double @main._Cfunc_log(double %0){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_log, align 8 -// ESCAPE: %2 = load ptr, ptr %1, align 8 -// ESCAPE: %3 = call double %2(double %0) -// ESCAPE: ret double %3 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_log, align 8 +// ESCAPE-NEXT: %2 = load ptr, ptr %1, align 8 +// ESCAPE-NEXT: %3 = call double %2(double %0) +// ESCAPE-NEXT: ret double %3 +// ESCAPE-NEXT: } // ESCAPE-LABEL: define i32 @main._Cfunc_puts(ptr %0){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_puts, align 8 -// ESCAPE: %2 = load ptr, ptr %1, align 8 -// ESCAPE: %3 = call i32 %2(ptr %0) -// ESCAPE: ret i32 %3 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_puts, align 8 +// ESCAPE-NEXT: %2 = load ptr, ptr %1, align 8 +// ESCAPE-NEXT: %3 = call i32 %2(ptr %0) +// ESCAPE-NEXT: ret i32 %3 +// ESCAPE-NEXT: } // ESCAPE-LABEL: define double @main._Cfunc_sin(double %0){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sin, align 8 -// ESCAPE: %2 = load ptr, ptr %1, align 8 -// ESCAPE: %3 = call double %2(double %0) -// ESCAPE: ret double %3 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sin, align 8 +// ESCAPE-NEXT: %2 = load ptr, ptr %1, align 8 +// ESCAPE-NEXT: %3 = call double %2(double %0) +// ESCAPE-NEXT: ret double %3 +// ESCAPE-NEXT: } // ESCAPE-LABEL: define double @main._Cfunc_sqrt(double %0){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sqrt, align 8 -// ESCAPE: %2 = load ptr, ptr %1, align 8 -// ESCAPE: %3 = call double %2(double %0) -// ESCAPE: ret double %3 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_sqrt, align 8 +// ESCAPE-NEXT: %2 = load ptr, ptr %1, align 8 +// ESCAPE-NEXT: %3 = call double %2(double %0) +// ESCAPE-NEXT: ret double %3 +// ESCAPE-NEXT: } // CHECK-LABEL: define void @main.init(){{.*}} { // CHECK-NEXT: _llgo_0: diff --git a/cl/_testgo/cgocfiles/cgocfiles.go b/cl/_testgo/cgocfiles/cgocfiles.go index 37ba4fc45c..8e4e67ccc8 100644 --- a/cl/_testgo/cgocfiles/cgocfiles.go +++ b/cl/_testgo/cgocfiles/cgocfiles.go @@ -17,12 +17,14 @@ import "fmt" // CHECK-NEXT: } // ESCAPE-LABEL: define i32 @main._Cfunc_test_structs(ptr %0, ptr %1, ptr %2, ptr %3, ptr %4){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %5 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_test_structs, align 8 -// ESCAPE: %6 = load ptr, ptr %5, align 8 -// ESCAPE: %7 = call i32 %6(ptr %0, ptr %1, ptr %2, ptr %3, ptr %4) -// ESCAPE: ret i32 %7 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %5 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_test_structs, align 8 +// ESCAPE-NEXT: %6 = load ptr, ptr %5, align 8 +// ESCAPE-NEXT: %7 = call i32 %6(ptr %0, ptr %1, ptr %2, ptr %3, ptr %4) +// ESCAPE-NEXT: ret i32 %7 +// ESCAPE-NEXT: } // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: diff --git a/cl/_testgo/cgodefer/cgodefer.go b/cl/_testgo/cgodefer/cgodefer.go index 0e0a68ae63..5f1b9cb856 100644 --- a/cl/_testgo/cgodefer/cgodefer.go +++ b/cl/_testgo/cgodefer/cgodefer.go @@ -16,12 +16,14 @@ import "C" // CHECK-NEXT: } // ESCAPE-LABEL: define [0 x i8] @main._Cfunc_free(ptr %0){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 -// ESCAPE: %2 = load ptr, ptr %1, align 8 -// ESCAPE: %3 = call [0 x i8] %2(ptr %0) -// ESCAPE: ret [0 x i8] %3 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_free, align 8 +// ESCAPE-NEXT: %2 = load ptr, ptr %1, align 8 +// ESCAPE-NEXT: %3 = call [0 x i8] %2(ptr %0) +// ESCAPE-NEXT: ret [0 x i8] %3 +// ESCAPE-NEXT: } // CHECK-LABEL: define void @main.main(){{.*}} { // CHECK-NEXT: _llgo_0: diff --git a/cl/_testgo/cgomacro/cgomacro.go b/cl/_testgo/cgomacro/cgomacro.go index 98f89c1a8f..ba1f6be461 100644 --- a/cl/_testgo/cgomacro/cgomacro.go +++ b/cl/_testgo/cgomacro/cgomacro.go @@ -93,48 +93,60 @@ import ( // CHECK-NEXT: } // ESCAPE-LABEL: define i32 @main._Cfunc_PyObject_Print(ptr %0, ptr %1, i32 %2){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %3 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyObject_Print, align 8 -// ESCAPE: %4 = load ptr, ptr %3, align 8 -// ESCAPE: %5 = call i32 %4(ptr %0, ptr %1, i32 %2) -// ESCAPE: ret i32 %5 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %3 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyObject_Print, align 8 +// ESCAPE-NEXT: %4 = load ptr, ptr %3, align 8 +// ESCAPE-NEXT: %5 = call i32 %4(ptr %0, ptr %1, i32 %2) +// ESCAPE-NEXT: ret i32 %5 +// ESCAPE-NEXT: } // ESCAPE-LABEL: define i32 @main._Cfunc_fputs(ptr %0, ptr %1){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_fputs, align 8 -// ESCAPE: %3 = load ptr, ptr %2, align 8 -// ESCAPE: %4 = call i32 %3(ptr %0, ptr %1) -// ESCAPE: ret i32 %4 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %2 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_fputs, align 8 +// ESCAPE-NEXT: %3 = load ptr, ptr %2, align 8 +// ESCAPE-NEXT: %4 = call i32 %3(ptr %0, ptr %1) +// ESCAPE-NEXT: ret i32 %4 +// ESCAPE-NEXT: } // ESCAPE-LABEL: define ptr @main._Cmacro_Py_False(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_False, align 8 -// ESCAPE: %1 = load ptr, ptr %0, align 8 -// ESCAPE: ret ptr %1 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_False, align 8 +// ESCAPE-NEXT: %1 = load ptr, ptr %0, align 8 +// ESCAPE-NEXT: ret ptr %1 +// ESCAPE-NEXT: } // ESCAPE-LABEL: define ptr @main._Cmacro_Py_None(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_None, align 8 -// ESCAPE: %1 = load ptr, ptr %0, align 8 -// ESCAPE: ret ptr %1 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_None, align 8 +// ESCAPE-NEXT: %1 = load ptr, ptr %0, align 8 +// ESCAPE-NEXT: ret ptr %1 +// ESCAPE-NEXT: } // ESCAPE-LABEL: define ptr @main._Cmacro_Py_True(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_True, align 8 -// ESCAPE: %1 = load ptr, ptr %0, align 8 -// ESCAPE: ret ptr %1 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_Py_True, align 8 +// ESCAPE-NEXT: %1 = load ptr, ptr %0, align 8 +// ESCAPE-NEXT: ret ptr %1 +// ESCAPE-NEXT: } // ESCAPE-LABEL: define ptr @main._Cmacro_stdout(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_stdout, align 8 -// ESCAPE: %1 = load ptr, ptr %0, align 8 -// ESCAPE: ret ptr %1 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %0 = load ptr, ptr @main._cgo_{{.*}}_Cmacro_stdout, align 8 +// ESCAPE-NEXT: %1 = load ptr, ptr %0, align 8 +// ESCAPE-NEXT: ret ptr %1 +// ESCAPE-NEXT: } // CHECK-LABEL: define void @main.init(){{.*}} { // CHECK-NEXT: _llgo_0: diff --git a/cl/_testgo/cgopython/cgopython.go b/cl/_testgo/cgopython/cgopython.go index a002de627a..88a245b2a3 100644 --- a/cl/_testgo/cgopython/cgopython.go +++ b/cl/_testgo/cgopython/cgopython.go @@ -17,12 +17,14 @@ import "C" // CHECK-NEXT: } // ESCAPE-LABEL: define i32 @main._Cfunc_PyRun_SimpleString(ptr %0){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 1 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyRun_SimpleString, align 8 -// ESCAPE: %2 = load ptr, ptr %1, align 8 -// ESCAPE: %3 = call i32 %2(ptr %0) -// ESCAPE: ret i32 %3 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 1 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %1 = load ptr, ptr @main._cgo_{{.*}}_Cfunc_PyRun_SimpleString, align 8 +// ESCAPE-NEXT: %2 = load ptr, ptr %1, align 8 +// ESCAPE-NEXT: %3 = call i32 %2(ptr %0) +// ESCAPE-NEXT: ret i32 %3 +// ESCAPE-NEXT: } // CHECK-LABEL: define [0 x i8] @main._Cfunc_Py_Finalize(){{.*}} { // CHECK-NEXT: _llgo_0: diff --git a/cl/_testgo/returnorder/in.go b/cl/_testgo/returnorder/in.go index 296ed56a5b..a7d8241ff5 100644 --- a/cl/_testgo/returnorder/in.go +++ b/cl/_testgo/returnorder/in.go @@ -37,15 +37,17 @@ func main() { // CHECK-NEXT: %5 = insertvalue { %main.state, i64 } %4, i64 %2, 1 // CHECK-NEXT: ret { %main.state, i64 } %5 // ESCAPE-LABEL: define { %main.state, i64 } @main.returnStateAndMut(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: %0 = getelementptr inbounds %main.state, ptr %.stack, i32 0, i32 0 -// ESCAPE: store i64 1, ptr %0, align 8 -// ESCAPE: %1 = call i64 @"main.(*state).mutate"(ptr %.stack, i64 2) -// ESCAPE: %2 = load %main.state, ptr %.stack, align 8 -// ESCAPE: %3 = insertvalue { %main.state, i64 } undef, %main.state %2, 0 -// ESCAPE: %4 = insertvalue { %main.state, i64 } %3, i64 %1, 1 -// ESCAPE: ret { %main.state, i64 } %4 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %0 = getelementptr inbounds %main.state, ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: store i64 1, ptr %0, align 8 +// ESCAPE-NEXT: %1 = call i64 @"main.(*state).mutate"(ptr %.stack, i64 2) +// ESCAPE-NEXT: %2 = load %main.state, ptr %.stack, align 8 +// ESCAPE-NEXT: %3 = insertvalue { %main.state, i64 } undef, %main.state %2, 0 +// ESCAPE-NEXT: %4 = insertvalue { %main.state, i64 } %3, i64 %1, 1 +// ESCAPE-NEXT: ret { %main.state, i64 } %4 +// ESCAPE-NEXT: } func returnStateAndMut() (state, int) { x := state{v: 1} return x, x.mutate(2) diff --git a/cl/_testgo/tprecurfn/in.go b/cl/_testgo/tprecurfn/in.go index 6abde1e97f..a36fa0925f 100644 --- a/cl/_testgo/tprecurfn/in.go +++ b/cl/_testgo/tprecurfn/in.go @@ -7,14 +7,23 @@ type My[T any] struct { } // ESCAPE-LABEL: define void @main.main(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 24, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) -// ESCAPE: %0 = getelementptr inbounds %"main.My[int]", ptr %.stack, i32 0, i32 1 -// ESCAPE: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) -// ESCAPE: store { ptr, ptr } { ptr @"__llgo_stub.main.main$1", ptr null }, ptr {{%.*}}, align 8 -// ESCAPE: store ptr %1, ptr %0, align 8 -// ESCAPE: call void {{%.*}}(ptr {{%.*}}, i64 100) -// ESCAPE: ret void +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 24, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) +// ESCAPE-NEXT: %0 = getelementptr inbounds %"main.My[int]", ptr %.stack, i32 0, i32 1 +// ESCAPE-NEXT: %1 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// ESCAPE-NEXT: %2 = getelementptr inbounds %"main.My[int]", ptr %1, i32 0, i32 0 +// ESCAPE-NEXT: store { ptr, ptr } { ptr @"__llgo_stub.main.main$1", ptr null }, ptr %2, align 8 +// ESCAPE-NEXT: store ptr %1, ptr %0, align 8 +// ESCAPE-NEXT: %3 = getelementptr inbounds %"main.My[int]", ptr %.stack, i32 0, i32 1 +// ESCAPE-NEXT: %4 = load ptr, ptr %3, align 8 +// ESCAPE-NEXT: %5 = getelementptr inbounds %"main.My[int]", ptr %4, i32 0, i32 0 +// ESCAPE-NEXT: %6 = load { ptr, ptr }, ptr %5, align 8 +// ESCAPE-NEXT: %7 = extractvalue { ptr, ptr } %6, 1 +// ESCAPE-NEXT: %8 = extractvalue { ptr, ptr } %6, 0 +// ESCAPE-NEXT: call void %8(ptr %7, i64 100) +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } // CHECK-LABEL: define void @main.main(){{.*}} { func main() { diff --git a/cl/_testgo/tptypes/in.go b/cl/_testgo/tptypes/in.go index d0e3b30115..03dfeabcc4 100644 --- a/cl/_testgo/tptypes/in.go +++ b/cl/_testgo/tptypes/in.go @@ -178,24 +178,131 @@ type ( // CHECK-NEXT: } // ESCAPE-LABEL: define void @main.main(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 24, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) -// ESCAPE: %16 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// ESCAPE: call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %.stack, %"{{.*}}/runtime/internal/runtime.Slice" {{%.*}}) -// ESCAPE: %.stack1 = alloca i8, i64 24, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 24, i1 false) -// ESCAPE: %22 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) -// ESCAPE: call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]string,string\]}}).Append"(ptr %.stack1, %"{{.*}}/runtime/internal/runtime.Slice" {{%.*}}) -// ESCAPE: %.stack2 = alloca i8, i64 24, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack2, i8 0, i64 24, i1 false) -// ESCAPE: %28 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) -// ESCAPE: call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %.stack2, %"{{.*}}/runtime/internal/runtime.Slice" {{%.*}}) -// ESCAPE: %37 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) -// ESCAPE: call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append2"(ptr %.stack2, %"{{.*}}/runtime/internal/runtime.Slice" {{%.*}}) -// ESCAPE: getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack, i32 0, i32 0 -// ESCAPE: getelementptr inbounds %"main.Slice{{\[\[\]string,string\]}}", ptr %.stack1, i32 0, i32 0 -// ESCAPE: getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack2, i32 0, i32 0 -// ESCAPE: ret void +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %0 = alloca %"main.Data[int]", align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %0, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %1 = getelementptr inbounds %"main.Data[int]", ptr %0, i32 0, i32 0 +// ESCAPE-NEXT: store i64 1, ptr %1, align 8 +// ESCAPE-NEXT: %2 = load %"main.Data[int]", ptr %0, align 8 +// ESCAPE-NEXT: %3 = extractvalue %"main.Data[int]" %2, 0 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %3) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %4 = alloca %"main.Data[string]", align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %4, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: %5 = getelementptr inbounds %"main.Data[string]", ptr %4, i32 0, i32 0 +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 5 }, ptr %5, align 8 +// ESCAPE-NEXT: %6 = load %"main.Data[string]", ptr %4, align 8 +// ESCAPE-NEXT: %7 = extractvalue %"main.Data[string]" %6, 0 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %7) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %8 = alloca %"main.Data[int]", align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %8, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %9 = getelementptr inbounds %"main.Data[int]", ptr %8, i32 0, i32 0 +// ESCAPE-NEXT: store i64 100, ptr %9, align 8 +// ESCAPE-NEXT: %10 = load %"main.Data[int]", ptr %8, align 8 +// ESCAPE-NEXT: %11 = extractvalue %"main.Data[int]" %10, 0 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %11) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %12 = alloca %"main.Data[string]", align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %12, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: %13 = getelementptr inbounds %"main.Data[string]", ptr %12, i32 0, i32 0 +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 5 }, ptr %13, align 8 +// ESCAPE-NEXT: %14 = load %"main.Data[string]", ptr %12, align 8 +// ESCAPE-NEXT: %15 = extractvalue %"main.Data[string]" %14, 0 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %15) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 0) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %.stack = alloca i8, i64 24, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 24, i1 false) +// ESCAPE-NEXT: %16 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// ESCAPE-NEXT: %17 = getelementptr inbounds i64, ptr %16, i64 0 +// ESCAPE-NEXT: store i64 100, ptr %17, align 8 +// ESCAPE-NEXT: %18 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %16, 0 +// ESCAPE-NEXT: %19 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %18, i64 1, 1 +// ESCAPE-NEXT: %20 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %19, i64 1, 2 +// ESCAPE-NEXT: %21 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %.stack, %"{{.*}}/runtime/internal/runtime.Slice" %20) +// ESCAPE-NEXT: %.stack1 = alloca i8, i64 24, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 24, i1 false) +// ESCAPE-NEXT: %22 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 16) +// ESCAPE-NEXT: %23 = getelementptr inbounds %"{{.*}}/runtime/internal/runtime.String", ptr %22, i64 0 +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 5 }, ptr %23, align 8 +// ESCAPE-NEXT: %24 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %22, 0 +// ESCAPE-NEXT: %25 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %24, i64 1, 1 +// ESCAPE-NEXT: %26 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %25, i64 1, 2 +// ESCAPE-NEXT: %27 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]string,string\]}}).Append"(ptr %.stack1, %"{{.*}}/runtime/internal/runtime.Slice" %26) +// ESCAPE-NEXT: %.stack2 = alloca i8, i64 24, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack2, i8 0, i64 24, i1 false) +// ESCAPE-NEXT: %28 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// ESCAPE-NEXT: %29 = getelementptr inbounds i64, ptr %28, i64 0 +// ESCAPE-NEXT: store i64 1, ptr %29, align 8 +// ESCAPE-NEXT: %30 = getelementptr inbounds i64, ptr %28, i64 1 +// ESCAPE-NEXT: store i64 2, ptr %30, align 8 +// ESCAPE-NEXT: %31 = getelementptr inbounds i64, ptr %28, i64 2 +// ESCAPE-NEXT: store i64 3, ptr %31, align 8 +// ESCAPE-NEXT: %32 = getelementptr inbounds i64, ptr %28, i64 3 +// ESCAPE-NEXT: store i64 4, ptr %32, align 8 +// ESCAPE-NEXT: %33 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %28, 0 +// ESCAPE-NEXT: %34 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %33, i64 4, 1 +// ESCAPE-NEXT: %35 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %34, i64 4, 2 +// ESCAPE-NEXT: %36 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append"(ptr %.stack2, %"{{.*}}/runtime/internal/runtime.Slice" %35) +// ESCAPE-NEXT: %37 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// ESCAPE-NEXT: %38 = getelementptr inbounds i64, ptr %37, i64 0 +// ESCAPE-NEXT: store i64 1, ptr %38, align 8 +// ESCAPE-NEXT: %39 = getelementptr inbounds i64, ptr %37, i64 1 +// ESCAPE-NEXT: store i64 2, ptr %39, align 8 +// ESCAPE-NEXT: %40 = getelementptr inbounds i64, ptr %37, i64 2 +// ESCAPE-NEXT: store i64 3, ptr %40, align 8 +// ESCAPE-NEXT: %41 = getelementptr inbounds i64, ptr %37, i64 3 +// ESCAPE-NEXT: store i64 4, ptr %41, align 8 +// ESCAPE-NEXT: %42 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %37, 0 +// ESCAPE-NEXT: %43 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %42, i64 4, 1 +// ESCAPE-NEXT: %44 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %43, i64 4, 2 +// ESCAPE-NEXT: %45 = call %"{{.*}}/runtime/internal/runtime.Slice" @"main.(*Slice{{\[\[\]int,int\]}}).Append2"(ptr %.stack2, %"{{.*}}/runtime/internal/runtime.Slice" %44) +// ESCAPE-NEXT: %46 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: %47 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %46, align 8 +// ESCAPE-NEXT: %48 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: %49 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %48, align 8 +// ESCAPE-NEXT: %50 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %49, 0 +// ESCAPE-NEXT: %51 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %49, 1 +// ESCAPE-NEXT: %52 = icmp uge i64 0, %51 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %52, i64 0, i1 true, i64 %51) +// ESCAPE-NEXT: %53 = getelementptr inbounds i64, ptr %50, i64 0 +// ESCAPE-NEXT: %54 = load i64, ptr %53, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %47) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %54) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %55 = getelementptr inbounds %"main.Slice{{\[\[\]string,string\]}}", ptr %.stack1, i32 0, i32 0 +// ESCAPE-NEXT: %56 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %55, align 8 +// ESCAPE-NEXT: %57 = getelementptr inbounds %"main.Slice{{\[\[\]string,string\]}}", ptr %.stack1, i32 0, i32 0 +// ESCAPE-NEXT: %58 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %57, align 8 +// ESCAPE-NEXT: %59 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %58, 0 +// ESCAPE-NEXT: %60 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %58, 1 +// ESCAPE-NEXT: %61 = icmp uge i64 0, %60 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %61, i64 0, i1 true, i64 %60) +// ESCAPE-NEXT: %62 = getelementptr inbounds %"{{.*}}/runtime/internal/runtime.String", ptr %59, i64 0 +// ESCAPE-NEXT: %63 = load %"{{.*}}/runtime/internal/runtime.String", ptr %62, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %56) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %63) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %64 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack2, i32 0, i32 0 +// ESCAPE-NEXT: %65 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %64, align 8 +// ESCAPE-NEXT: %66 = getelementptr inbounds %"main.Slice{{\[\[\]int,int\]}}", ptr %.stack2, i32 0, i32 0 +// ESCAPE-NEXT: %67 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %66, align 8 +// ESCAPE-NEXT: %68 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %67, 0 +// ESCAPE-NEXT: %69 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %67, 1 +// ESCAPE-NEXT: %70 = icmp uge i64 0, %69 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %70, i64 0, i1 true, i64 %69) +// ESCAPE-NEXT: %71 = getelementptr inbounds i64, ptr %68, i64 0 +// ESCAPE-NEXT: %72 = load i64, ptr %71, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintSlice"(%"{{.*}}/runtime/internal/runtime.Slice" %65) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %72) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } func main() { println(DataInt{1}.v) diff --git a/cl/_testlibc/atomic/in.go b/cl/_testlibc/atomic/in.go index 267a414a1a..2318a9ece9 100644 --- a/cl/_testlibc/atomic/in.go +++ b/cl/_testlibc/atomic/in.go @@ -7,15 +7,30 @@ import ( ) // ESCAPE-LABEL: define void @main.main(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: store atomic i64 100, ptr %.stack seq_cst, align 8 -// ESCAPE: load atomic i64, ptr %.stack seq_cst, align 8 -// ESCAPE: atomicrmw add ptr %.stack, i64 1 seq_cst, align 8 -// ESCAPE: cmpxchg ptr %.stack, i64 100, i64 102 seq_cst seq_cst, align 8 -// ESCAPE: cmpxchg ptr %.stack, i64 101, i64 102 seq_cst seq_cst, align 8 -// ESCAPE: atomicrmw sub ptr %.stack, i64 1 seq_cst, align 8 -// ESCAPE: ret void +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: store atomic i64 100, ptr %.stack seq_cst, align 8 +// ESCAPE-NEXT: %0 = load atomic i64, ptr %.stack seq_cst, align 8 +// ESCAPE-NEXT: %1 = call i32 (ptr, ...) @printf(ptr @0, i64 %0) +// ESCAPE-NEXT: %2 = atomicrmw add ptr %.stack, i64 1 seq_cst, align 8 +// ESCAPE-NEXT: %3 = load i64, ptr %.stack, align 8 +// ESCAPE-NEXT: %4 = call i32 (ptr, ...) @printf(ptr @1, i64 %2, i64 %3) +// ESCAPE-NEXT: %5 = cmpxchg ptr %.stack, i64 100, i64 102 seq_cst seq_cst, align 8 +// ESCAPE-NEXT: %6 = extractvalue { i64, i1 } %5, 0 +// ESCAPE-NEXT: %7 = extractvalue { i64, i1 } %5, 1 +// ESCAPE-NEXT: %8 = load i64, ptr %.stack, align 8 +// ESCAPE-NEXT: %9 = call i32 (ptr, ...) @printf(ptr @2, i64 %6, i64 %8) +// ESCAPE-NEXT: %10 = cmpxchg ptr %.stack, i64 101, i64 102 seq_cst seq_cst, align 8 +// ESCAPE-NEXT: %11 = extractvalue { i64, i1 } %10, 0 +// ESCAPE-NEXT: %12 = extractvalue { i64, i1 } %10, 1 +// ESCAPE-NEXT: %13 = load i64, ptr %.stack, align 8 +// ESCAPE-NEXT: %14 = call i32 (ptr, ...) @printf(ptr @3, i64 %11, i64 %13) +// ESCAPE-NEXT: %15 = atomicrmw sub ptr %.stack, i64 1 seq_cst, align 8 +// ESCAPE-NEXT: %16 = load i64, ptr %.stack, align 8 +// ESCAPE-NEXT: %17 = call i32 (ptr, ...) @printf(ptr @4, i64 %15, i64 %16) +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } // CHECK-LABEL: define void @main.main(){{.*}} { func main() { diff --git a/cl/_testlibgo/atomic/in.go b/cl/_testlibgo/atomic/in.go index 1cf2913eee..55213eafe7 100644 --- a/cl/_testlibgo/atomic/in.go +++ b/cl/_testlibgo/atomic/in.go @@ -6,15 +6,61 @@ import ( ) // ESCAPE-LABEL: define void @main.main(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: store atomic i64 100, ptr %.stack seq_cst, align 8 -// ESCAPE: load atomic i64, ptr %.stack seq_cst, align 8 -// ESCAPE: atomicrmw add ptr %.stack, i64 1 seq_cst, align 8 -// ESCAPE: cmpxchg ptr %.stack, i64 100, i64 102 seq_cst seq_cst, align 8 -// ESCAPE: cmpxchg ptr %.stack, i64 101, i64 102 seq_cst seq_cst, align 8 -// ESCAPE: atomicrmw add ptr %.stack, i64 -1 seq_cst, align 8 -// ESCAPE: ret void +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: store atomic i64 100, ptr %.stack seq_cst, align 8 +// ESCAPE-NEXT: %0 = load atomic i64, ptr %.stack seq_cst, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 6 }) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %0) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %1 = atomicrmw add ptr %.stack, i64 1 seq_cst, align 8 +// ESCAPE-NEXT: %2 = add i64 %1, 1 +// ESCAPE-NEXT: %3 = load i64, ptr %.stack, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" { ptr @1, i64 4 }) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %2) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" { ptr @2, i64 2 }) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %3) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %4 = cmpxchg ptr %.stack, i64 100, i64 102 seq_cst seq_cst, align 8 +// ESCAPE-NEXT: %5 = extractvalue { i64, i1 } %4, 1 +// ESCAPE-NEXT: %6 = load i64, ptr %.stack, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" { ptr @3, i64 4 }) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %5) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" { ptr @2, i64 2 }) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %6) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %7 = cmpxchg ptr %.stack, i64 101, i64 102 seq_cst seq_cst, align 8 +// ESCAPE-NEXT: %8 = extractvalue { i64, i1 } %7, 1 +// ESCAPE-NEXT: %9 = load i64, ptr %.stack, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" { ptr @3, i64 4 }) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %8) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" { ptr @2, i64 2 }) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %9) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %10 = atomicrmw add ptr %.stack, i64 -1 seq_cst, align 8 +// ESCAPE-NEXT: %11 = add i64 %10, -1 +// ESCAPE-NEXT: %12 = load i64, ptr %.stack, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" { ptr @1, i64 4 }) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %11) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" { ptr @2, i64 2 }) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %12) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } // CHECK-LABEL: define void @main.main(){{.*}} { func main() { diff --git a/cl/_testrt/abitype/in.go b/cl/_testrt/abitype/in.go index f82f9a0ca1..7e84ab3717 100644 --- a/cl/_testrt/abitype/in.go +++ b/cl/_testrt/abitype/in.go @@ -52,17 +52,49 @@ import ( // CHECK-NEXT: } // ESCAPE-LABEL: define void @main.main(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 16, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) -// ESCAPE: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 4) -// ESCAPE: store %"{{.*}}/runtime/internal/runtime.eface" {{%.*}}, ptr %.stack, align 8 -// ESCAPE: getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 -// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 1) -// ESCAPE: store %"{{.*}}/runtime/internal/runtime.eface" {{%.*}}, ptr %.stack, align 8 -// ESCAPE: getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 -// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) -// ESCAPE: ret void +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 16, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: %0 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 4) +// ESCAPE-NEXT: store i32 0, ptr %0, align 4 +// ESCAPE-NEXT: %1 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_int32, ptr undef }, ptr %0, 1 +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %1, ptr %.stack, align 8 +// ESCAPE-NEXT: %2 = getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: %3 = load ptr, ptr %2, align 8 +// ESCAPE-NEXT: %4 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/abi.(*Type).String"(ptr %3) +// ESCAPE-NEXT: %5 = call i1 @"{{.*}}/runtime/internal/runtime.StringEqual"(%"{{.*}}/runtime/internal/runtime.String" %4, %"{{.*}}/runtime/internal/runtime.String" { ptr @0, i64 5 }) +// ESCAPE-NEXT: %6 = xor i1 %5, true +// ESCAPE-NEXT: br i1 %6, label %_llgo_1, label %_llgo_2 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_1: ; preds = %_llgo_0 +// ESCAPE-NEXT: %7 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @1, i64 14 }, ptr %7, align 8 +// ESCAPE-NEXT: %8 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %7, 1 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %8) +// ESCAPE-NEXT: unreachable +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_2: ; preds = %_llgo_0 +// ESCAPE-NEXT: %9 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 1) +// ESCAPE-NEXT: store i8 0, ptr %9, align 1 +// ESCAPE-NEXT: %10 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_uint8, ptr undef }, ptr %9, 1 +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %10, ptr %.stack, align 8 +// ESCAPE-NEXT: %11 = getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: %12 = load ptr, ptr %11, align 8 +// ESCAPE-NEXT: %13 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/abi.(*Type).String"(ptr %12) +// ESCAPE-NEXT: %14 = call i1 @"{{.*}}/runtime/internal/runtime.StringEqual"(%"{{.*}}/runtime/internal/runtime.String" %13, %"{{.*}}/runtime/internal/runtime.String" { ptr @3, i64 5 }) +// ESCAPE-NEXT: %15 = xor i1 %14, true +// ESCAPE-NEXT: br i1 %15, label %_llgo_3, label %_llgo_4 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_3: ; preds = %_llgo_2 +// ESCAPE-NEXT: %16 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @4, i64 14 }, ptr %16, align 8 +// ESCAPE-NEXT: %17 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %16, 1 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %17) +// ESCAPE-NEXT: unreachable +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_4: ; preds = %_llgo_2 +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } type eface struct { typ *abi.Type diff --git a/cl/_testrt/eface/in.go b/cl/_testrt/eface/in.go index 1d3ad19c7b..5904566981 100644 --- a/cl/_testrt/eface/in.go +++ b/cl/_testrt/eface/in.go @@ -27,13 +27,15 @@ func (t *T) Invoke() { // CHECK-NEXT: ret void // CHECK-NEXT: } // ESCAPE-LABEL: define void @main.dump(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 16, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) -// ESCAPE: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 -// ESCAPE: %1 = getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 -// ESCAPE: %2 = load ptr, ptr %1, align 8 -// ESCAPE: call void @main.dumpTyp(ptr %2, %"{{.*}}/runtime/internal/runtime.String" zeroinitializer) -// ESCAPE: ret void +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 16, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 +// ESCAPE-NEXT: %1 = getelementptr inbounds %main.eface, ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: %2 = load ptr, ptr %1, align 8 +// ESCAPE-NEXT: call void @main.dumpTyp(ptr %2, %"{{.*}}/runtime/internal/runtime.String" zeroinitializer) +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } func dump(v any) { e := (*eface)(unsafe.Pointer(&v)) dumpTyp(e._type, "") diff --git a/cl/_testrt/float2any/in.go b/cl/_testrt/float2any/in.go index 81763b91e5..f15412a6ee 100644 --- a/cl/_testrt/float2any/in.go +++ b/cl/_testrt/float2any/in.go @@ -19,13 +19,59 @@ type u64parts struct { } // ESCAPE-LABEL: define void @main.check32(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 16, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) -// ESCAPE: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 -// ESCAPE: load %"{{.*}}/runtime/internal/runtime.eface", ptr %.stack, align 8 -// ESCAPE: icmp eq ptr {{.*}}, @_llgo_float32 -// ESCAPE: load i32, ptr -// ESCAPE: icmp ne i32 {{.*}}, 1078530011 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 16, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 +// ESCAPE-NEXT: %1 = load %"{{.*}}/runtime/internal/runtime.eface", ptr %.stack, align 8 +// ESCAPE-NEXT: %2 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %1, 0 +// ESCAPE-NEXT: %3 = icmp eq ptr %2, @_llgo_float32 +// ESCAPE-NEXT: br i1 %3, label %_llgo_5, label %_llgo_6 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_1: ; preds = %_llgo_7 +// ESCAPE-NEXT: %4 = alloca %main.eface, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %4, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: %5 = load %main.eface, ptr %.stack, align 8 +// ESCAPE-NEXT: store %main.eface %5, ptr %4, align 8 +// ESCAPE-NEXT: %6 = getelementptr inbounds %main.eface, ptr %4, i32 0, i32 1 +// ESCAPE-NEXT: %7 = load ptr, ptr %6, align 8 +// ESCAPE-NEXT: %8 = load i32, ptr %7, align 4 +// ESCAPE-NEXT: %9 = icmp ne i32 %8, 1078530011 +// ESCAPE-NEXT: br i1 %9, label %_llgo_3, label %_llgo_4 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_2: ; preds = %_llgo_7 +// ESCAPE-NEXT: %10 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @1, i64 14 }, ptr %10, align 8 +// ESCAPE-NEXT: %11 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %10, 1 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %11) +// ESCAPE-NEXT: unreachable +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_3: ; preds = %_llgo_1 +// ESCAPE-NEXT: %12 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @3, i64 14 }, ptr %12, align 8 +// ESCAPE-NEXT: %13 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %12, 1 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %13) +// ESCAPE-NEXT: unreachable +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_4: ; preds = %_llgo_1 +// ESCAPE-NEXT: ret void +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_5: ; preds = %_llgo_0 +// ESCAPE-NEXT: %14 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %1, 1 +// ESCAPE-NEXT: %15 = load float, ptr %14, align 4 +// ESCAPE-NEXT: %16 = insertvalue { float, i1 } undef, float %15, 0 +// ESCAPE-NEXT: %17 = insertvalue { float, i1 } %16, i1 true, 1 +// ESCAPE-NEXT: br label %_llgo_7 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_6: ; preds = %_llgo_0 +// ESCAPE-NEXT: br label %_llgo_7 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_7: ; preds = %_llgo_6, %_llgo_5 +// ESCAPE-NEXT: %18 = phi { float, i1 } [ %17, %_llgo_5 ], [ zeroinitializer, %_llgo_6 ] +// ESCAPE-NEXT: %19 = extractvalue { float, i1 } %18, 0 +// ESCAPE-NEXT: %20 = extractvalue { float, i1 } %18, 1 +// ESCAPE-NEXT: br i1 %20, label %_llgo_1, label %_llgo_2 +// ESCAPE-NEXT: } // CHECK-LABEL: define void @main.check32(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { // CHECK: icmp eq ptr {{.*}}, @_llgo_float32 @@ -44,15 +90,70 @@ func check32(v any) { } // ESCAPE-LABEL: define void @main.check64(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 16, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) -// ESCAPE: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 -// ESCAPE: load %"{{.*}}/runtime/internal/runtime.eface", ptr %.stack, align 8 -// ESCAPE: icmp eq ptr {{.*}}, @_llgo_float64 -// ESCAPE: load i32, ptr -// ESCAPE: icmp ne i32 {{.*}}, 1405670641 -// ESCAPE: load i32, ptr -// ESCAPE: icmp ne i32 {{.*}}, 1074340347 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 16, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 +// ESCAPE-NEXT: %1 = load %"{{.*}}/runtime/internal/runtime.eface", ptr %.stack, align 8 +// ESCAPE-NEXT: %2 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %1, 0 +// ESCAPE-NEXT: %3 = icmp eq ptr %2, @_llgo_float64 +// ESCAPE-NEXT: br i1 %3, label %_llgo_6, label %_llgo_7 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_1: ; preds = %_llgo_8 +// ESCAPE-NEXT: %4 = alloca %main.eface, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %4, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: %5 = load %main.eface, ptr %.stack, align 8 +// ESCAPE-NEXT: store %main.eface %5, ptr %4, align 8 +// ESCAPE-NEXT: %6 = alloca %main.u64parts, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %6, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: %7 = getelementptr inbounds %main.eface, ptr %4, i32 0, i32 1 +// ESCAPE-NEXT: %8 = load ptr, ptr %7, align 8 +// ESCAPE-NEXT: %9 = load %main.u64parts, ptr %8, align 4 +// ESCAPE-NEXT: store %main.u64parts %9, ptr %6, align 4 +// ESCAPE-NEXT: %10 = getelementptr inbounds %main.u64parts, ptr %6, i32 0, i32 0 +// ESCAPE-NEXT: %11 = load i32, ptr %10, align 4 +// ESCAPE-NEXT: %12 = icmp ne i32 %11, 1405670641 +// ESCAPE-NEXT: br i1 %12, label %_llgo_3, label %_llgo_5 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_2: ; preds = %_llgo_8 +// ESCAPE-NEXT: %13 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @5, i64 14 }, ptr %13, align 8 +// ESCAPE-NEXT: %14 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %13, 1 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %14) +// ESCAPE-NEXT: unreachable +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_3: ; preds = %_llgo_5, %_llgo_1 +// ESCAPE-NEXT: %15 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @6, i64 14 }, ptr %15, align 8 +// ESCAPE-NEXT: %16 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %15, 1 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %16) +// ESCAPE-NEXT: unreachable +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_4: ; preds = %_llgo_5 +// ESCAPE-NEXT: ret void +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_5: ; preds = %_llgo_1 +// ESCAPE-NEXT: %17 = getelementptr inbounds %main.u64parts, ptr %6, i32 0, i32 1 +// ESCAPE-NEXT: %18 = load i32, ptr %17, align 4 +// ESCAPE-NEXT: %19 = icmp ne i32 %18, 1074340347 +// ESCAPE-NEXT: br i1 %19, label %_llgo_3, label %_llgo_4 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_6: ; preds = %_llgo_0 +// ESCAPE-NEXT: %20 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %1, 1 +// ESCAPE-NEXT: %21 = load double, ptr %20, align 8 +// ESCAPE-NEXT: %22 = insertvalue { double, i1 } undef, double %21, 0 +// ESCAPE-NEXT: %23 = insertvalue { double, i1 } %22, i1 true, 1 +// ESCAPE-NEXT: br label %_llgo_8 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_7: ; preds = %_llgo_0 +// ESCAPE-NEXT: br label %_llgo_8 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_8: ; preds = %_llgo_7, %_llgo_6 +// ESCAPE-NEXT: %24 = phi { double, i1 } [ %23, %_llgo_6 ], [ zeroinitializer, %_llgo_7 ] +// ESCAPE-NEXT: %25 = extractvalue { double, i1 } %24, 0 +// ESCAPE-NEXT: %26 = extractvalue { double, i1 } %24, 1 +// ESCAPE-NEXT: br i1 %26, label %_llgo_1, label %_llgo_2 +// ESCAPE-NEXT: } // CHECK-LABEL: define void @main.check64(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { // CHECK: icmp eq ptr {{.*}}, @_llgo_float64 diff --git a/cl/_testrt/funcaddr/in.go b/cl/_testrt/funcaddr/in.go index 3a36f13734..566c193903 100644 --- a/cl/_testrt/funcaddr/in.go +++ b/cl/_testrt/funcaddr/in.go @@ -42,15 +42,29 @@ func add(a, b int) int { // CHECK-NEXT: ret void // CHECK-NEXT: } // ESCAPE-LABEL: define void @main.main(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: store ptr @main.add, ptr %.stack, align 8 -// ESCAPE: %.stack1 = alloca i8, i64 8, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 8, i1 false) -// ESCAPE: store ptr @"main.main$1", ptr %.stack1, align 8 -// ESCAPE: load ptr, ptr %.stack, align 8 -// ESCAPE: load ptr, ptr %.stack1, align 8 -// ESCAPE: ret void +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: store ptr @main.add, ptr %.stack, align 8 +// ESCAPE-NEXT: %.stack1 = alloca i8, i64 8, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: store ptr @"main.main$1", ptr %.stack1, align 8 +// ESCAPE-NEXT: %0 = load ptr, ptr %.stack, align 8 +// ESCAPE-NEXT: %1 = icmp eq ptr @main.add, %0 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %1) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %2 = load ptr, ptr %.stack, align 8 +// ESCAPE-NEXT: %3 = load ptr, ptr %.stack, align 8 +// ESCAPE-NEXT: %4 = icmp eq ptr %2, %3 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %4) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %5 = load ptr, ptr %.stack1, align 8 +// ESCAPE-NEXT: %6 = load ptr, ptr %.stack1, align 8 +// ESCAPE-NEXT: %7 = icmp eq ptr %5, %6 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintBool"(i1 %7) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } func main() { var fn Add = add // CHECK-LABEL: define i64 @"main.main$1"(i64 %0, i64 %1){{.*}} { diff --git a/cl/_testrt/funcdecl/in.go b/cl/_testrt/funcdecl/in.go index 86a9dbe48e..000c84adac 100644 --- a/cl/_testrt/funcdecl/in.go +++ b/cl/_testrt/funcdecl/in.go @@ -83,14 +83,16 @@ func check(fn func()) { // CHECK-NEXT: } // ESCAPE-LABEL: define ptr @main.closurePtr(%"{{.*}}/runtime/internal/runtime.eface" %0){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 16, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) -// ESCAPE: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 -// ESCAPE: %1 = getelementptr inbounds %main.rtype, ptr %.stack, i32 0, i32 1 -// ESCAPE: %2 = load ptr, ptr %1, align 8 -// ESCAPE: %3 = getelementptr inbounds { ptr, ptr }, ptr %2, i32 0, i32 0 -// ESCAPE: %4 = load ptr, ptr %3, align 8 -// ESCAPE: ret ptr %4 +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 16, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.eface" %0, ptr %.stack, align 8 +// ESCAPE-NEXT: %1 = getelementptr inbounds %main.rtype, ptr %.stack, i32 0, i32 1 +// ESCAPE-NEXT: %2 = load ptr, ptr %1, align 8 +// ESCAPE-NEXT: %3 = getelementptr inbounds { ptr, ptr }, ptr %2, i32 0, i32 0 +// ESCAPE-NEXT: %4 = load ptr, ptr %3, align 8 +// ESCAPE-NEXT: ret ptr %4 +// ESCAPE-NEXT: } func closurePtr(a any) unsafe.Pointer { return (*rtype)(unsafe.Pointer(&a)).ptr.fn diff --git a/cl/_testrt/index/in.go b/cl/_testrt/index/in.go index 915b933d7f..310e6a798a 100644 --- a/cl/_testrt/index/in.go +++ b/cl/_testrt/index/in.go @@ -154,14 +154,133 @@ type S []int // CHECK-NEXT: } // ESCAPE-LABEL: define void @main.main(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 16, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) -// ESCAPE: getelementptr inbounds i64, ptr %.stack, i64 0 -// ESCAPE: getelementptr inbounds i64, ptr %.stack, i64 1 -// ESCAPE: load i64, ptr {{%.*}}, align 8 -// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) -// ESCAPE: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange" -// ESCAPE: ret void +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %0 = alloca %main.point, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %0, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: %1 = alloca [3 x %main.point], align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %1, i8 0, i64 48, i1 false) +// ESCAPE-NEXT: %2 = getelementptr inbounds %main.point, ptr %1, i64 0 +// ESCAPE-NEXT: %3 = getelementptr inbounds %main.point, ptr %2, i32 0, i32 0 +// ESCAPE-NEXT: %4 = getelementptr inbounds %main.point, ptr %2, i32 0, i32 1 +// ESCAPE-NEXT: %5 = getelementptr inbounds %main.point, ptr %1, i64 1 +// ESCAPE-NEXT: %6 = getelementptr inbounds %main.point, ptr %5, i32 0, i32 0 +// ESCAPE-NEXT: %7 = getelementptr inbounds %main.point, ptr %5, i32 0, i32 1 +// ESCAPE-NEXT: %8 = getelementptr inbounds %main.point, ptr %1, i64 2 +// ESCAPE-NEXT: %9 = getelementptr inbounds %main.point, ptr %8, i32 0, i32 0 +// ESCAPE-NEXT: %10 = getelementptr inbounds %main.point, ptr %8, i32 0, i32 1 +// ESCAPE-NEXT: store i64 1, ptr %3, align 8 +// ESCAPE-NEXT: store i64 2, ptr %4, align 8 +// ESCAPE-NEXT: store i64 3, ptr %6, align 8 +// ESCAPE-NEXT: store i64 4, ptr %7, align 8 +// ESCAPE-NEXT: store i64 5, ptr %9, align 8 +// ESCAPE-NEXT: store i64 6, ptr %10, align 8 +// ESCAPE-NEXT: %11 = load [3 x %main.point], ptr %1, align 8 +// ESCAPE-NEXT: %12 = getelementptr inbounds %main.point, ptr %1, i64 2 +// ESCAPE-NEXT: %13 = load %main.point, ptr %12, align 8 +// ESCAPE-NEXT: store %main.point %13, ptr %0, align 8 +// ESCAPE-NEXT: %14 = getelementptr inbounds %main.point, ptr %0, i32 0, i32 0 +// ESCAPE-NEXT: %15 = load i64, ptr %14, align 8 +// ESCAPE-NEXT: %16 = getelementptr inbounds %main.point, ptr %0, i32 0, i32 1 +// ESCAPE-NEXT: %17 = load i64, ptr %16, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %15) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %17) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %18 = alloca [2 x i64], align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %18, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: %19 = alloca [2 x [2 x i64]], align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %19, i8 0, i64 32, i1 false) +// ESCAPE-NEXT: %20 = getelementptr inbounds [2 x i64], ptr %19, i64 0 +// ESCAPE-NEXT: %21 = icmp eq ptr %20, null +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.AssertNilDeref"(i1 %21) +// ESCAPE-NEXT: %22 = getelementptr inbounds i64, ptr %20, i64 0 +// ESCAPE-NEXT: %23 = icmp eq ptr %20, null +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.AssertNilDeref"(i1 %23) +// ESCAPE-NEXT: %24 = getelementptr inbounds i64, ptr %20, i64 1 +// ESCAPE-NEXT: %25 = getelementptr inbounds [2 x i64], ptr %19, i64 1 +// ESCAPE-NEXT: %26 = icmp eq ptr %25, null +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.AssertNilDeref"(i1 %26) +// ESCAPE-NEXT: %27 = getelementptr inbounds i64, ptr %25, i64 0 +// ESCAPE-NEXT: %28 = icmp eq ptr %25, null +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.AssertNilDeref"(i1 %28) +// ESCAPE-NEXT: %29 = getelementptr inbounds i64, ptr %25, i64 1 +// ESCAPE-NEXT: store i64 1, ptr %22, align 8 +// ESCAPE-NEXT: store i64 2, ptr %24, align 8 +// ESCAPE-NEXT: store i64 3, ptr %27, align 8 +// ESCAPE-NEXT: store i64 4, ptr %29, align 8 +// ESCAPE-NEXT: %30 = load [2 x [2 x i64]], ptr %19, align 8 +// ESCAPE-NEXT: %31 = getelementptr inbounds [2 x i64], ptr %19, i64 1 +// ESCAPE-NEXT: %32 = load [2 x i64], ptr %31, align 8 +// ESCAPE-NEXT: store [2 x i64] %32, ptr %18, align 8 +// ESCAPE-NEXT: %33 = getelementptr inbounds i64, ptr %18, i64 0 +// ESCAPE-NEXT: %34 = load i64, ptr %33, align 8 +// ESCAPE-NEXT: %35 = getelementptr inbounds i64, ptr %18, i64 1 +// ESCAPE-NEXT: %36 = load i64, ptr %35, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %34) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %36) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %37 = alloca [5 x i64], align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %37, i8 0, i64 40, i1 false) +// ESCAPE-NEXT: %38 = getelementptr inbounds i64, ptr %37, i64 0 +// ESCAPE-NEXT: %39 = getelementptr inbounds i64, ptr %37, i64 1 +// ESCAPE-NEXT: %40 = getelementptr inbounds i64, ptr %37, i64 2 +// ESCAPE-NEXT: %41 = getelementptr inbounds i64, ptr %37, i64 3 +// ESCAPE-NEXT: %42 = getelementptr inbounds i64, ptr %37, i64 4 +// ESCAPE-NEXT: store i64 1, ptr %38, align 8 +// ESCAPE-NEXT: store i64 2, ptr %39, align 8 +// ESCAPE-NEXT: store i64 3, ptr %40, align 8 +// ESCAPE-NEXT: store i64 4, ptr %41, align 8 +// ESCAPE-NEXT: store i64 5, ptr %42, align 8 +// ESCAPE-NEXT: %43 = load [5 x i64], ptr %37, align 8 +// ESCAPE-NEXT: %44 = getelementptr inbounds i64, ptr %37, i64 2 +// ESCAPE-NEXT: %45 = load i64, ptr %44, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %45) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %46 = load i8, ptr getelementptr inbounds (i8, ptr @0, i64 2), align 1 +// ESCAPE-NEXT: %47 = zext i8 %46 to i64 +// ESCAPE-NEXT: %48 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/internal/runtime.StringFromUint64"(i64 %47) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %48) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %49 = load i8, ptr getelementptr inbounds (i8, ptr @0, i64 1), align 1 +// ESCAPE-NEXT: %50 = zext i8 %49 to i64 +// ESCAPE-NEXT: %51 = call %"{{.*}}/runtime/internal/runtime.String" @"{{.*}}/runtime/internal/runtime.StringFromUint64"(i64 %50) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintString"(%"{{.*}}/runtime/internal/runtime.String" %51) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %.stack = alloca i8, i64 16, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 16, i1 false) +// ESCAPE-NEXT: %52 = getelementptr inbounds i64, ptr %.stack, i64 0 +// ESCAPE-NEXT: %53 = getelementptr inbounds i64, ptr %.stack, i64 1 +// ESCAPE-NEXT: store i64 1, ptr %52, align 8 +// ESCAPE-NEXT: store i64 2, ptr %53, align 8 +// ESCAPE-NEXT: %54 = getelementptr inbounds i64, ptr %.stack, i64 1 +// ESCAPE-NEXT: %55 = load i64, ptr %54, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %55) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %56 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 32) +// ESCAPE-NEXT: %57 = getelementptr inbounds i64, ptr %56, i64 0 +// ESCAPE-NEXT: store i64 1, ptr %57, align 8 +// ESCAPE-NEXT: %58 = getelementptr inbounds i64, ptr %56, i64 1 +// ESCAPE-NEXT: store i64 2, ptr %58, align 8 +// ESCAPE-NEXT: %59 = getelementptr inbounds i64, ptr %56, i64 2 +// ESCAPE-NEXT: store i64 3, ptr %59, align 8 +// ESCAPE-NEXT: %60 = getelementptr inbounds i64, ptr %56, i64 3 +// ESCAPE-NEXT: store i64 4, ptr %60, align 8 +// ESCAPE-NEXT: %61 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %56, 0 +// ESCAPE-NEXT: %62 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %61, i64 4, 1 +// ESCAPE-NEXT: %63 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %62, i64 4, 2 +// ESCAPE-NEXT: %64 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %63, 0 +// ESCAPE-NEXT: %65 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %63, 1 +// ESCAPE-NEXT: %66 = icmp uge i64 1, %65 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.CheckIndexRange"(i1 %66, i64 1, i1 true, i64 %65) +// ESCAPE-NEXT: %67 = getelementptr inbounds i64, ptr %64, i64 1 +// ESCAPE-NEXT: %68 = load i64, ptr %67, align 8 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %68) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 0) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } func main() { a := [...]point{{1, 2}, {3, 4}, {5, 6}}[2] diff --git a/cl/_testrt/len/in.go b/cl/_testrt/len/in.go index 53e9ec9ee0..7bd5d2cf2b 100644 --- a/cl/_testrt/len/in.go +++ b/cl/_testrt/len/in.go @@ -99,27 +99,97 @@ type data struct { // CHECK-NEXT: ret void // CHECK-NEXT: } // ESCAPE-LABEL: define void @main.main(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 56, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 56, i1 false) -// ESCAPE: getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 0 -// ESCAPE: getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 1 -// ESCAPE: getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 2 -// ESCAPE: getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 3 -// ESCAPE: %.stack1 = alloca i8, i64 56, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 56, i1 false) -// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.NewChan"(i64 8, i64 2) -// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.MakeMap" -// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 8) -// ESCAPE: call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) -// ESCAPE: store %"{{.*}}/runtime/internal/runtime.String" {{.*}}, ptr {{%.*}}, align 8 -// ESCAPE: store ptr {{%.*}}, ptr {{%.*}}, align 8 -// ESCAPE: store ptr {{%.*}}, ptr {{%.*}}, align 8 -// ESCAPE: store %"{{.*}}/runtime/internal/runtime.Slice" {{%.*}}, ptr {{%.*}}, align 8 -// ESCAPE: getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 0 -// ESCAPE: getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 1 -// ESCAPE: getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 2 -// ESCAPE: getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 3 -// ESCAPE: ret void +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 56, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 56, i1 false) +// ESCAPE-NEXT: %0 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: %1 = load %"{{.*}}/runtime/internal/runtime.String", ptr %0, align 8 +// ESCAPE-NEXT: %2 = extractvalue %"{{.*}}/runtime/internal/runtime.String" %1, 1 +// ESCAPE-NEXT: %3 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 1 +// ESCAPE-NEXT: %4 = load ptr, ptr %3, align 8 +// ESCAPE-NEXT: %5 = call i64 @"{{.*}}/runtime/internal/runtime.ChanLen"(ptr %4) +// ESCAPE-NEXT: %6 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 2 +// ESCAPE-NEXT: %7 = load ptr, ptr %6, align 8 +// ESCAPE-NEXT: %8 = call i64 @"{{.*}}/runtime/internal/runtime.MapLen"(ptr %7) +// ESCAPE-NEXT: %9 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 3 +// ESCAPE-NEXT: %10 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %9, align 8 +// ESCAPE-NEXT: %11 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %10, 1 +// ESCAPE-NEXT: %12 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 1 +// ESCAPE-NEXT: %13 = load ptr, ptr %12, align 8 +// ESCAPE-NEXT: %14 = call i64 @"{{.*}}/runtime/internal/runtime.ChanCap"(ptr %13) +// ESCAPE-NEXT: %15 = getelementptr inbounds %main.data, ptr %.stack, i32 0, i32 3 +// ESCAPE-NEXT: %16 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %15, align 8 +// ESCAPE-NEXT: %17 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %16, 2 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %2) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %5) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %8) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %11) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %14) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %17) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: %.stack1 = alloca i8, i64 56, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack1, i8 0, i64 56, i1 false) +// ESCAPE-NEXT: %18 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 0 +// ESCAPE-NEXT: %19 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 1 +// ESCAPE-NEXT: %20 = call ptr @"{{.*}}/runtime/internal/runtime.NewChan"(i64 8, i64 2) +// ESCAPE-NEXT: %21 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 2 +// ESCAPE-NEXT: %22 = call ptr @"{{.*}}/runtime/internal/runtime.MakeMap"(ptr @"map[_llgo_int]_llgo_string", i64 1) +// ESCAPE-NEXT: %23 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 8) +// ESCAPE-NEXT: store i64 1, ptr %23, align 8 +// ESCAPE-NEXT: %24 = call ptr @"{{.*}}/runtime/internal/runtime.MapAssign"(ptr @"map[_llgo_int]_llgo_string", ptr %22, ptr %23) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @16, i64 5 }, ptr %24, align 8 +// ESCAPE-NEXT: %25 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 3 +// ESCAPE-NEXT: %26 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 24) +// ESCAPE-NEXT: %27 = getelementptr inbounds i64, ptr %26, i64 0 +// ESCAPE-NEXT: store i64 1, ptr %27, align 8 +// ESCAPE-NEXT: %28 = getelementptr inbounds i64, ptr %26, i64 1 +// ESCAPE-NEXT: store i64 2, ptr %28, align 8 +// ESCAPE-NEXT: %29 = getelementptr inbounds i64, ptr %26, i64 2 +// ESCAPE-NEXT: store i64 3, ptr %29, align 8 +// ESCAPE-NEXT: %30 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" undef, ptr %26, 0 +// ESCAPE-NEXT: %31 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %30, i64 3, 1 +// ESCAPE-NEXT: %32 = insertvalue %"{{.*}}/runtime/internal/runtime.Slice" %31, i64 3, 2 +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @16, i64 5 }, ptr %18, align 8 +// ESCAPE-NEXT: store ptr %20, ptr %19, align 8 +// ESCAPE-NEXT: store ptr %22, ptr %21, align 8 +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.Slice" %32, ptr %25, align 8 +// ESCAPE-NEXT: %33 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 0 +// ESCAPE-NEXT: %34 = load %"{{.*}}/runtime/internal/runtime.String", ptr %33, align 8 +// ESCAPE-NEXT: %35 = extractvalue %"{{.*}}/runtime/internal/runtime.String" %34, 1 +// ESCAPE-NEXT: %36 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 1 +// ESCAPE-NEXT: %37 = load ptr, ptr %36, align 8 +// ESCAPE-NEXT: %38 = call i64 @"{{.*}}/runtime/internal/runtime.ChanLen"(ptr %37) +// ESCAPE-NEXT: %39 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 2 +// ESCAPE-NEXT: %40 = load ptr, ptr %39, align 8 +// ESCAPE-NEXT: %41 = call i64 @"{{.*}}/runtime/internal/runtime.MapLen"(ptr %40) +// ESCAPE-NEXT: %42 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 3 +// ESCAPE-NEXT: %43 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %42, align 8 +// ESCAPE-NEXT: %44 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %43, 1 +// ESCAPE-NEXT: %45 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 1 +// ESCAPE-NEXT: %46 = load ptr, ptr %45, align 8 +// ESCAPE-NEXT: %47 = call i64 @"{{.*}}/runtime/internal/runtime.ChanCap"(ptr %46) +// ESCAPE-NEXT: %48 = getelementptr inbounds %main.data, ptr %.stack1, i32 0, i32 3 +// ESCAPE-NEXT: %49 = load %"{{.*}}/runtime/internal/runtime.Slice", ptr %48, align 8 +// ESCAPE-NEXT: %50 = extractvalue %"{{.*}}/runtime/internal/runtime.Slice" %49, 2 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %35) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %38) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %41) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %44) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %47) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 32) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintInt"(i64 %50) +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.PrintByte"(i8 10) +// ESCAPE-NEXT: ret void +// ESCAPE-NEXT: } func main() { d := &data{} println(len(d.s), len(d.c), len(d.m), len(d.a), cap(d.c), cap(d.a)) diff --git a/cl/_testrt/vamethod/in.go b/cl/_testrt/vamethod/in.go index f49b89bca4..4ef7e50c11 100644 --- a/cl/_testrt/vamethod/in.go +++ b/cl/_testrt/vamethod/in.go @@ -118,18 +118,69 @@ type IFmt interface { // CHECK-NEXT: br i1 %38, label %_llgo_2, label %_llgo_1 // CHECK-NEXT: } // ESCAPE-LABEL: define void @main.main(){{.*}} { -// ESCAPE: %.stack = alloca i8, i64 8, align 8 -// ESCAPE: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) -// ESCAPE: call void @"main.(*CFmt).SetFormat"(ptr %.stack, ptr @0) -// ESCAPE: getelementptr inbounds %main.CFmt, ptr %.stack, i32 0, i32 0 -// ESCAPE: call i32 (ptr, ...) @printf -// ESCAPE: call void @"main.(*CFmt).SetFormat"(ptr %.stack, ptr @2) -// ESCAPE: getelementptr inbounds %main.CFmt, ptr %.stack, i32 0, i32 0 -// ESCAPE: call i32 (ptr, ...) @printf -// ESCAPE: %6 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) -// ESCAPE: insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @"*_llgo_main.CFmt", ptr undef }, ptr %6, 1 -// ESCAPE: call i1 @"{{.*}}/runtime/internal/runtime.Implements" -// ESCAPE: ret void +// ESCAPE-NEXT: _llgo_0: +// ESCAPE-NEXT: %.stack = alloca i8, i64 8, align 8 +// ESCAPE-NEXT: call void @llvm.memset.p0.i64(ptr %.stack, i8 0, i64 8, i1 false) +// ESCAPE-NEXT: call void @"main.(*CFmt).SetFormat"(ptr %.stack, ptr @0) +// ESCAPE-NEXT: %0 = getelementptr inbounds %main.CFmt, ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: %1 = load ptr, ptr %0, align 8 +// ESCAPE-NEXT: %2 = call i32 (ptr, ...) @printf(ptr %1, ptr @1, i64 100) +// ESCAPE-NEXT: call void @"main.(*CFmt).SetFormat"(ptr %.stack, ptr @2) +// ESCAPE-NEXT: %3 = getelementptr inbounds %main.CFmt, ptr %.stack, i32 0, i32 0 +// ESCAPE-NEXT: %4 = load ptr, ptr %3, align 8 +// ESCAPE-NEXT: %5 = call i32 (ptr, ...) @printf(ptr %4, i64 200, ptr @3) +// ESCAPE-NEXT: %6 = call ptr @"{{.*}}/runtime/internal/runtime.AllocZ"(i64 8) +// ESCAPE-NEXT: %7 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @"*_llgo_main.CFmt", ptr undef }, ptr %6, 1 +// ESCAPE-NEXT: %8 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %7, 0 +// ESCAPE-NEXT: %9 = call i1 @"{{.*}}/runtime/internal/runtime.Implements"(ptr @_llgo_main.IFmt, ptr %8) +// ESCAPE-NEXT: br i1 %9, label %_llgo_3, label %_llgo_4 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_1: ; preds = %_llgo_5 +// ESCAPE-NEXT: %10 = call ptr @"{{.*}}/runtime/internal/runtime.AllocU"(i64 16) +// ESCAPE-NEXT: store %"{{.*}}/runtime/internal/runtime.String" { ptr @20, i64 5 }, ptr %10, align 8 +// ESCAPE-NEXT: %11 = insertvalue %"{{.*}}/runtime/internal/runtime.eface" { ptr @_llgo_string, ptr undef }, ptr %10, 1 +// ESCAPE-NEXT: call void @"{{.*}}/runtime/internal/runtime.Panic"(%"{{.*}}/runtime/internal/runtime.eface" %11) +// ESCAPE-NEXT: unreachable +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_2: ; preds = %_llgo_5 +// ESCAPE-NEXT: %12 = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %36) +// ESCAPE-NEXT: %13 = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %36, 0 +// ESCAPE-NEXT: %14 = getelementptr ptr, ptr %13, i64 4 +// ESCAPE-NEXT: %15 = load ptr, ptr %14, align 8 +// ESCAPE-NEXT: %16 = insertvalue { ptr, ptr } undef, ptr %15, 0 +// ESCAPE-NEXT: %17 = insertvalue { ptr, ptr } %16, ptr %12, 1 +// ESCAPE-NEXT: %18 = extractvalue { ptr, ptr } %17, 1 +// ESCAPE-NEXT: %19 = extractvalue { ptr, ptr } %17, 0 +// ESCAPE-NEXT: call void %19(ptr %18, ptr @18) +// ESCAPE-NEXT: %20 = call ptr @"{{.*}}/runtime/internal/runtime.IfacePtrData"(%"{{.*}}/runtime/internal/runtime.iface" %36) +// ESCAPE-NEXT: %21 = extractvalue %"{{.*}}/runtime/internal/runtime.iface" %36, 0 +// ESCAPE-NEXT: %22 = getelementptr ptr, ptr %21, i64 3 +// ESCAPE-NEXT: %23 = load ptr, ptr %22, align 8 +// ESCAPE-NEXT: %24 = insertvalue { ptr, ptr } undef, ptr %23, 0 +// ESCAPE-NEXT: %25 = insertvalue { ptr, ptr } %24, ptr %20, 1 +// ESCAPE-NEXT: %26 = extractvalue { ptr, ptr } %25, 1 +// ESCAPE-NEXT: %27 = extractvalue { ptr, ptr } %25, 0 +// ESCAPE-NEXT: %28 = call i32 (ptr, ...) %27(ptr %26, ptr @19, i64 100, i64 200) +// ESCAPE-NEXT: ret void +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_3: ; preds = %_llgo_0 +// ESCAPE-NEXT: %29 = extractvalue %"{{.*}}/runtime/internal/runtime.eface" %7, 1 +// ESCAPE-NEXT: %30 = call ptr @"{{.*}}/runtime/internal/runtime.NewItab"(ptr @"_llgo_iface$a85zs5wWQQoPIERm_en8plssh4spdIeeXZPC-E0TDh0", ptr %8) +// ESCAPE-NEXT: %31 = insertvalue %"{{.*}}/runtime/internal/runtime.iface" undef, ptr %30, 0 +// ESCAPE-NEXT: %32 = insertvalue %"{{.*}}/runtime/internal/runtime.iface" %31, ptr %29, 1 +// ESCAPE-NEXT: %33 = insertvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } undef, %"{{.*}}/runtime/internal/runtime.iface" %32, 0 +// ESCAPE-NEXT: %34 = insertvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %33, i1 true, 1 +// ESCAPE-NEXT: br label %_llgo_5 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_4: ; preds = %_llgo_0 +// ESCAPE-NEXT: br label %_llgo_5 +// ESCAPE-EMPTY: +// ESCAPE-NEXT: _llgo_5: ; preds = %_llgo_4, %_llgo_3 +// ESCAPE-NEXT: %35 = phi { %"{{.*}}/runtime/internal/runtime.iface", i1 } [ %34, %_llgo_3 ], [ zeroinitializer, %_llgo_4 ] +// ESCAPE-NEXT: %36 = extractvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %35, 0 +// ESCAPE-NEXT: %37 = extractvalue { %"{{.*}}/runtime/internal/runtime.iface", i1 } %35, 1 +// ESCAPE-NEXT: br i1 %37, label %_llgo_2, label %_llgo_1 +// ESCAPE-NEXT: } func main() { cfmt := &CFmt{} cfmt.SetFormat(c.Str("%s (%d)\n")) From 230c1ca725cbdafa144ce2c6a47daf29a67d05c4 Mon Sep 17 00:00:00 2001 From: Rick Guo Date: Sat, 1 Aug 2026 21:42:19 +0800 Subject: [PATCH 10/12] fix(escape): reject pointer comparisons --- internal/escape/escape.go | 11 ++--------- internal/escape/testdata/calls/out.txt | 8 ++++---- internal/escape/testdata/safe-uses/out.txt | 8 ++++---- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/internal/escape/escape.go b/internal/escape/escape.go index a747bffa75..3c789c2e71 100644 --- a/internal/escape/escape.go +++ b/internal/escape/escape.go @@ -238,7 +238,7 @@ func (a *analyzer) classifyNoCaptureUse(state useState) useAction { return useSafe } case opcodeAtomicCmpXchg: - if state.operand == 0 || state.operand == 1 { + if state.operand == 0 { return useSafe } case llvm.VAArg: @@ -257,8 +257,6 @@ func (a *analyzer) classifyNoCaptureUse(state useState) useAction { if state.operand == 0 { return useFollow } - case llvm.ICmp: - return useSafe } return useCapture } @@ -356,17 +354,12 @@ func (a *analyzer) allocationUses(root llvm.Value) walkResult { if state.operand == 0 { return useFollow } - case llvm.ICmp: - return useSafe case opcodeAtomicCmpXchg: - switch state.operand { - case 0: + if state.operand == 0 { if user.Alignment() > result.alignment { result.alignment = user.Alignment() } return useSafe - case 1: - return useSafe } case opcodeAtomicRMW: if state.operand == 0 { diff --git a/internal/escape/testdata/calls/out.txt b/internal/escape/testdata/calls/out.txt index 69b87ac00a..4cbc8c350f 100644 --- a/internal/escape/testdata/calls/out.txt +++ b/internal/escape/testdata/calls/out.txt @@ -222,8 +222,8 @@ entry: define void @known_derived(i1 %cond) { entry: - %p.stack = alloca i8, i64 8, align 1 - call void @derived(ptr %p.stack, i1 %cond) + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @derived(ptr %p, i1 %cond) ret void } @@ -236,8 +236,8 @@ entry: define void @known_atomic() { entry: - %p.stack = alloca i8, i64 8, align 4 - call void @atomic_use(ptr %p.stack) + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + call void @atomic_use(ptr %p) ret void } diff --git a/internal/escape/testdata/safe-uses/out.txt b/internal/escape/testdata/safe-uses/out.txt index 6b057b6c34..c7d6fc3967 100644 --- a/internal/escape/testdata/safe-uses/out.txt +++ b/internal/escape/testdata/safe-uses/out.txt @@ -67,8 +67,8 @@ exit: ; preds = %loop define i1 @compare() { entry: - %p.stack = alloca i8, i64 8, align 1 - %same = icmp eq ptr %p.stack, null + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %same = icmp eq ptr %p, null ret i1 %same } @@ -110,8 +110,8 @@ entry: define void @cmpxchg_expected() { entry: - %p.stack = alloca i8, i64 8, align 1 - %old = cmpxchg ptr @escaped_pointer, ptr %p.stack, ptr null monotonic monotonic, align 8 + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %old = cmpxchg ptr @escaped_pointer, ptr %p, ptr null monotonic monotonic, align 8 ret void } From 20e4f4fc326bb6ad3e935ebc95f09e60a9a54041 Mon Sep 17 00:00:00 2001 From: Rick Guo Date: Sat, 1 Aug 2026 22:54:47 +0800 Subject: [PATCH 11/12] fix(escape): update conservative comparison tests --- internal/build/build.go | 4 +- internal/build/build_test.go | 16 +++++++ internal/build/escape_phase1_test.go | 45 ------------------- .../build/testdata/escape_phase1/escape.go | 5 +-- internal/escape/escape.go | 3 +- internal/escape/escape_test.go | 4 +- internal/escape/testdata/calls/in.txt | 16 +++++++ internal/escape/testdata/calls/out.txt | 17 +++++++ internal/llgen/llgenf_test.go | 7 --- 9 files changed, 54 insertions(+), 63 deletions(-) delete mode 100644 internal/build/escape_phase1_test.go diff --git a/internal/build/build.go b/internal/build/build.go index 7fedb8c70e..0b9df5b57e 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -1837,9 +1837,7 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { ctx.cTransformer.SetSkipFuncs(cabiSkipFuncsForPlan9Asm(ctx, pkgPath, ret.Module())) if !ctx.buildConf.DisableEscapeAnalysis { - if err := llescape.TransformModule(ret.Module()); err != nil { - return fmt.Errorf("run escape analysis for %v failed: %w", pkgPath, err) - } + llescape.TransformModule(ret.Module()) } llabi.LowerLargeAggregates(ctx.prog.TargetData(), ret.Module()) ctx.cTransformer.TransformModule(ret.Path(), ret.Module()) diff --git a/internal/build/build_test.go b/internal/build/build_test.go index 5d67a571b2..178fd25c6b 100644 --- a/internal/build/build_test.go +++ b/internal/build/build_test.go @@ -85,6 +85,22 @@ func TestConfigCloneDoesNotAliasInput(t *testing.T) { } } +func TestDisableEscapeAnalysis(t *testing.T) { + conf := NewDefaultConf(ModeGen) + conf.DisableEscapeAnalysis = true + pkgs, err := Do([]string{"./testdata/escape_phase1"}, conf) + if err != nil { + t.Fatal(err) + } + if len(pkgs) != 1 || pkgs[0].LPkg == nil { + t.Fatalf("generated packages = %#v", pkgs) + } + defer pkgs[0].LPkg.Prog.Dispose() + if ir := pkgs[0].LPkg.String(); !strings.Contains(ir, `call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocZ"`) { + t.Fatalf("disabled escape analysis changed heap allocation:\n%s", ir) + } +} + func TestResolveBuildConfigDefaultsAndValidation(t *testing.T) { resolved, err := resolveBuildConfig(&Config{ BuildMode: BuildModeCArchive, diff --git a/internal/build/escape_phase1_test.go b/internal/build/escape_phase1_test.go deleted file mode 100644 index f5881a29a5..0000000000 --- a/internal/build/escape_phase1_test.go +++ /dev/null @@ -1,45 +0,0 @@ -//go:build !llgo - -package build - -import ( - "strings" - "testing" - - "github.com/xgo-dev/llvm" -) - -func TestEscapePhase1RunsAfterModuleHook(t *testing.T) { - conf := NewDefaultConf(ModeGen) - var before string - conf.ModuleHook = func(pkg Package) { - if fn := findLLVMFunction(pkg.LPkg.Module(), ".Local"); !fn.IsNil() { - before = fn.String() - } - } - pkgs, err := Do([]string{"./testdata/escape_phase1"}, conf) - if err != nil { - t.Fatalf("generate escape Phase 1 fixture: %v", err) - } - if len(pkgs) != 1 || pkgs[0].LPkg == nil { - t.Fatalf("generated packages = %#v", pkgs) - } - defer pkgs[0].LPkg.Prog.Dispose() - - if !strings.Contains(before, "runtime.AllocZ") { - t.Fatalf("pre-transform Local has no heap allocation:\n%s", before) - } - after := findLLVMFunction(pkgs[0].LPkg.Module(), ".Local").String() - if strings.Contains(after, "runtime.AllocZ") || !strings.Contains(after, "alloca i8") { - t.Fatalf("post-transform Local was not moved to the stack:\n%s", after) - } -} - -func findLLVMFunction(mod llvm.Module, suffix string) llvm.Value { - for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { - if strings.HasSuffix(fn.Name(), suffix) { - return fn - } - } - return llvm.Value{} -} diff --git a/internal/build/testdata/escape_phase1/escape.go b/internal/build/testdata/escape_phase1/escape.go index 7c4371b57f..20b124b181 100644 --- a/internal/build/testdata/escape_phase1/escape.go +++ b/internal/build/testdata/escape_phase1/escape.go @@ -1,11 +1,10 @@ package escapephase1 -func read(p *int) int { - return *p +func read(*int) int { + return 7 } func Local() int { p := new(int) - *p = 7 return read(p) } diff --git a/internal/escape/escape.go b/internal/escape/escape.go index 3c789c2e71..299fb3426e 100644 --- a/internal/escape/escape.go +++ b/internal/escape/escape.go @@ -429,7 +429,7 @@ type allocationPlan struct { // TransformModule analyzes eligible LLGo allocations and rewrites proven-local // AllocZ and AllocU calls. -func TransformModule(mod llvm.Module) error { +func TransformModule(mod llvm.Module) { a := newAnalyzer(mod) a.solveNoCapture() @@ -451,7 +451,6 @@ func TransformModule(mod llvm.Module) error { for _, plan := range plans { rewriteAllocation(mod.Context(), plan) } - return nil } func (a *analyzer) planAllocation(instr llvm.Value) (allocationPlan, bool) { diff --git a/internal/escape/escape_test.go b/internal/escape/escape_test.go index d152ce0769..c57a2f2179 100644 --- a/internal/escape/escape_test.go +++ b/internal/escape/escape_test.go @@ -34,9 +34,7 @@ func TestTransformModule(t *testing.T) { t.Fatalf("parse %s: %v", input, err) } defer mod.Dispose() - if err := TransformModule(mod); err != nil { - t.Fatal(err) - } + TransformModule(mod) output := filepath.Join(dir, "out.txt") want, err := os.ReadFile(output) diff --git a/internal/escape/testdata/calls/in.txt b/internal/escape/testdata/calls/in.txt index ef80ea7094..b4c02e9505 100644 --- a/internal/escape/testdata/calls/in.txt +++ b/internal/escape/testdata/calls/in.txt @@ -5,6 +5,7 @@ target datalayout = "e-p:64:64-i64:64-n32:64-S128" declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) declare void @external(ptr) +declare void @assert_nil(i1) define void @read(ptr %p) { entry: @@ -18,6 +19,14 @@ entry: ret void } +define i8 @nil_checked_read(ptr %p) { +entry: + %nil = icmp eq ptr %p, null + call void @assert_nil(i1 %nil) + %v = load i8, ptr %p, align 1 + ret i8 %v +} + define void @save(ptr %p) { entry: store ptr %p, ptr @escaped @@ -156,6 +165,13 @@ entry: ret void } +define i8 @known_nil_checked_read() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %v = call i8 @nil_checked_read(ptr %p) + ret i8 %v +} + define void @known_content_leak() { entry: %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) diff --git a/internal/escape/testdata/calls/out.txt b/internal/escape/testdata/calls/out.txt index 4cbc8c350f..03ebae6efb 100644 --- a/internal/escape/testdata/calls/out.txt +++ b/internal/escape/testdata/calls/out.txt @@ -9,6 +9,8 @@ declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) declare void @external(ptr) +declare void @assert_nil(i1) + define void @read(ptr %p) { entry: %v = load i8, ptr %p, align 8 @@ -21,6 +23,14 @@ entry: ret void } +define i8 @nil_checked_read(ptr %p) { +entry: + %nil = icmp eq ptr %p, null + call void @assert_nil(i1 %nil) + %v = load i8, ptr %p, align 1 + ret i8 %v +} + define void @save(ptr %p) { entry: store ptr %p, ptr @escaped, align 8 @@ -162,6 +172,13 @@ entry: ret void } +define i8 @known_nil_checked_read() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %v = call i8 @nil_checked_read(ptr %p) + ret i8 %v +} + define void @known_content_leak() { entry: %p.stack = alloca i8, i64 8, align 8 diff --git a/internal/llgen/llgenf_test.go b/internal/llgen/llgenf_test.go index 6740b551d4..006e0c5e1e 100644 --- a/internal/llgen/llgenf_test.go +++ b/internal/llgen/llgenf_test.go @@ -62,10 +62,3 @@ func TestApplyGoBuildFlagsFileErrorIncludesPath(t *testing.T) { t.Fatalf("applyGoBuildFlagsFile(%q) error = %v, want path", data, err) } } - -func TestGenFromDisablesEscapeAnalysis(t *testing.T) { - ir := GenFrom("../build/testdata/escape_phase1") - if !strings.Contains(ir, "runtime.AllocZ") { - t.Fatalf("GenFrom output has no pre-transform heap allocation:\n%s", ir) - } -} From 5df423ea436b02931b13d00fd1b6bd51848c726d Mon Sep 17 00:00:00 2001 From: Rick Guo Date: Sun, 2 Aug 2026 23:23:35 +0800 Subject: [PATCH 12/12] feat(escape): add exact copy analysis and diagnostics --- cmd/internal/compile/compile.go | 7 +- cmd/internal/compile/compile_test.go | 44 +- internal/build/build.go | 63 +- internal/escape/escape.go | 361 ++++++-- internal/escape/escape_test.go | 29 +- internal/escape/pointer_info.go | 804 ++++++++++++++++++ .../testdata/diagnostics/diagnostics.txt | 12 + internal/escape/testdata/diagnostics/in.txt | 54 ++ internal/escape/testdata/diagnostics/out.txt | 56 ++ internal/escape/testdata/exact-copy/in.txt | 169 ++++ internal/escape/testdata/exact-copy/out.txt | 188 +++- 11 files changed, 1727 insertions(+), 60 deletions(-) create mode 100644 internal/escape/pointer_info.go create mode 100644 internal/escape/testdata/diagnostics/diagnostics.txt create mode 100644 internal/escape/testdata/diagnostics/in.txt create mode 100644 internal/escape/testdata/diagnostics/out.txt diff --git a/cmd/internal/compile/compile.go b/cmd/internal/compile/compile.go index e9b4b12833..c7c10b24db 100644 --- a/cmd/internal/compile/compile.go +++ b/cmd/internal/compile/compile.go @@ -163,6 +163,7 @@ func runCmd(_ *base.Command, args []string) { conf.NoErrorColumn = opts.noColumns.value != 0 conf.AllowNoBody = !opts.complete conf.DisableBoundsChecks = opts.noBounds.value != 0 + conf.EscapeDiagnostics = opts.showOpt.value != 0 var loaderCompilerFlags []string if opts.allErrors.value != 0 { loaderCompilerFlags = append(loaderCompilerFlags, "-e") @@ -183,6 +184,11 @@ func runCmd(_ *base.Command, args []string) { conf.OptLevel = optlevel.O0 } pkgs, err := build.Do(files, conf) + if len(pkgs) != 0 { + for _, message := range pkgs[0].EscapeDiagnosticMessages { + fmt.Fprintln(os.Stderr, message) + } + } if len(pkgs) != 0 && pkgs[0].LPkg != nil { pkgs[0].LPkg.Prog.Dispose() } @@ -200,7 +206,6 @@ func (opts *options) unsupported() []string { } } appendFlag(opts.dynlink, "-dynlink") - appendFlag(opts.showOpt.value != 0, "-m") appendFlag(opts.live, "-live") appendFlag(opts.race, "-race") appendFlag(opts.smallFrames, "-smallframes") diff --git a/cmd/internal/compile/compile_test.go b/cmd/internal/compile/compile_test.go index 5b2988aa22..501a1cadc8 100644 --- a/cmd/internal/compile/compile_test.go +++ b/cmd/internal/compile/compile_test.go @@ -19,6 +19,7 @@ func TestGoCompilerFlagNamesAndTypes(t *testing.T) { "-C", "-e", "-l=4", + "-m=2", "-lang=go1.17", "-d=panic,ssa/check/on", "-p=p", @@ -28,7 +29,7 @@ func TestGoCompilerFlagNamesAndTypes(t *testing.T) { if err != nil { t.Fatal(err) } - if opts.noBounds.value != 1 || opts.concurrency != 2 || opts.noColumns.value != 1 || opts.allErrors.value != 1 || opts.noInline.value != 4 { + if opts.noBounds.value != 1 || opts.concurrency != 2 || opts.noColumns.value != 1 || opts.allErrors.value != 1 || opts.noInline.value != 4 || opts.showOpt.value != 2 { t.Fatalf("parsed flags: %+v", opts) } if opts.lang != "go1.17" || opts.pkgPath != "p" || opts.importCfg != "importcfg" { @@ -105,7 +106,7 @@ func TestUnsupportedCompilerFlags(t *testing.T) { writeBar: countFlag{set: true}, debug: stringListFlag{"panic,libfuzzer", "ssa/check/on,ssa/check/seed=1,wb"}, } - want := []string{"-dynlink", "-m", "-live", "-race", "-smallframes", "-+", "-wb", "-d=libfuzzer", "-d=wb"} + want := []string{"-dynlink", "-live", "-race", "-smallframes", "-+", "-wb", "-d=libfuzzer", "-d=wb"} if got := opts.unsupported(); !reflect.DeepEqual(got, want) { t.Fatalf("unsupported=%v, want %v", got, want) } @@ -182,6 +183,45 @@ func TestRunCmdBuildsAndReportsErrors(t *testing.T) { } } +func TestRunCmdEscapeDiagnostics(t *testing.T) { + dir := t.TempDir() + source := dir + "/escape.go" + if err := os.WriteFile(source, []byte(`package escape +var sink *int +func noescape(p *int) { _ = *p } +func leak(p *int) { sink = p } +func content(p **int) { sink = *p } +func result(p *int) *int { return p } +func through(p *int) *int { return result(p) } +func discard(p *int) { _ = result(p) } +func mutate(p *int) { *p = 1 } +`), 0o644); err != nil { + t.Fatal(err) + } + + _, stderr, code := runCompileCommand(t, []string{"-C", "-m", "-l", source}) + if code != 0 { + t.Fatalf("-m compile exit code = %d, stderr=%q", code, stderr) + } + for _, want := range []string{ + // TODO: Re-enable these diagnostics after the summary traversal handles + // compiler-generated `icmp eq ptr, null` checks without recording a heap + // flow. HeapToStack cannot generally discard the comparison because the + // current AllocZ/AllocU runtime contracts do not both guarantee non-nil. + // "escape.go:3: p does not escape", + "escape.go:4: leaking param: p", + // "escape.go:5: leaking param content: p", + "escape.go:6: leaking param: p to result ~r0 level=0", + "escape.go:7: leaking param: p to result ~r0 level=0", + "escape.go:8: p does not escape", + "escape.go:9: p does not escape", + } { + if !strings.Contains(stderr, want) { + t.Errorf("-m stderr missing %q:\n%s", want, stderr) + } + } +} + func runCompileCommand(t *testing.T, args []string) (stdout, stderr string, exitCode int) { t.Helper() outFile, err := os.CreateTemp(t.TempDir(), "stdout") diff --git a/internal/build/build.go b/internal/build/build.go index 0b9df5b57e..f010bfaf60 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -38,6 +38,7 @@ import ( "sync/atomic" "golang.org/x/tools/go/ssa" + "golang.org/x/tools/go/ssa/ssautil" "github.com/goplus/llgo/cl" llabi "github.com/goplus/llgo/internal/abi" @@ -191,6 +192,8 @@ type Config struct { DisableBoundsChecks bool // DisableEscapeAnalysis skips the heap-to-stack transform. DisableEscapeAnalysis bool + // EscapeDiagnostics requests source-positioned parameter escape summaries. + EscapeDiagnostics bool // PthreadStackSize sets a custom stack size, in bytes, for pthread-backed // goroutines. A zero value keeps the platform pthread default. @@ -1837,7 +1840,10 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { ctx.cTransformer.SetSkipFuncs(cabiSkipFuncsForPlan9Asm(ctx, pkgPath, ret.Module())) if !ctx.buildConf.DisableEscapeAnalysis { - llescape.TransformModule(ret.Module()) + result := llescape.TransformModule(ret.Module(), ctx.buildConf.EscapeDiagnostics) + if ctx.buildConf.EscapeDiagnostics { + aPkg.EscapeDiagnosticMessages = escapeDiagnosticMessages(aPkg, result, ctx.buildConf.NoErrorColumn) + } } llabi.LowerLargeAggregates(ctx.prog.TargetData(), ret.Module()) ctx.cTransformer.TransformModule(ret.Path(), ret.Module()) @@ -1914,6 +1920,59 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error { return nil } +func escapeDiagnosticMessages(pkg *aPackage, result llescape.Result, noColumn bool) []string { + functions := make(map[string]*ssa.Function) + for fn := range ssautil.AllFunctions(pkg.SSA.Prog) { + if fn == nil || fn.Pkg != pkg.SSA || fn.Parent() != nil || fn.Synthetic != "" || fn.Origin() != nil || fn.Signature.Recv() != nil { + continue + } + functions[fn.String()] = fn + } + + var messages []string + for _, summary := range result.Parameters { + fn := functions[summary.Function] + if fn == nil || summary.Parameter >= len(fn.Params) { + continue + } + param := fn.Params[summary.Parameter] + name := param.Name() + pos := pkg.Fset.Position(param.Pos()) + if name == "" || !pos.IsValid() { + continue + } + + var reports []string + if summary.HeapLevel == 0 { + reports = append(reports, "leaking param: "+name) + } else if summary.HeapLevel > 0 { + reports = append(reports, "leaking param content: "+name) + } + for _, leak := range summary.Results { + if leak.Result >= fn.Signature.Results().Len() { + continue + } + resultName := fn.Signature.Results().At(leak.Result).Name() + if resultName == "" { + resultName = fmt.Sprintf("~r%d", leak.Result) + } + reports = append(reports, fmt.Sprintf("leaking param: %s to result %s level=%d", name, resultName, leak.Level)) + } + if len(reports) == 0 { + reports = append(reports, name+" does not escape") + } + + position := fmt.Sprintf("%s:%d", pos.Filename, pos.Line) + if !noColumn { + position += fmt.Sprintf(":%d", pos.Column) + } + for _, report := range reports { + messages = append(messages, position+": "+report) + } + } + return messages +} + func printCompiledPackage(conf *Config, pkg *aPackage) { if conf.PrintPackages && !pkg.CacheHit { fmt.Fprintln(os.Stderr, pkg.PkgPath) @@ -2175,6 +2234,8 @@ type aPackage struct { Meta *meta.PackageMeta rewriteVars map[string]string + EscapeDiagnosticMessages []string + // Cache related fields Fingerprint string // fingerprint digest Manifest string // manifest text content diff --git a/internal/escape/escape.go b/internal/escape/escape.go index 299fb3426e..dd855b5143 100644 --- a/internal/escape/escape.go +++ b/internal/escape/escape.go @@ -3,6 +3,8 @@ package escape import ( + "math" + "sort" "strings" llabi "github.com/goplus/llgo/internal/abi" @@ -26,40 +28,173 @@ type paramKey struct { param int } +type locationEdgeKind uint8 + +const ( + locationFlow locationEdgeKind = iota + locationDeref + locationHeap + locationMutator + locationCallee +) + +type location struct { + value llvm.Value + result int +} + type locationEdge struct { - from llvm.Value - to llvm.Value - operand int + to location + kind locationEdgeKind } // locationGraph records diagnostic pointer flows but is never queried by the // optimization analyses. type locationGraph struct { - edges map[locationEdge]struct{} + edges map[location]map[locationEdge]struct{} } func newLocationGraph() locationGraph { - return locationGraph{edges: make(map[locationEdge]struct{})} + return locationGraph{edges: make(map[location]map[locationEdge]struct{})} +} + +func valueLocation(value llvm.Value) location { + return location{value: value, result: -1} } -func (g *locationGraph) addUse(from llvm.Value, state useState) { - g.edges[locationEdge{from: from, to: state.user, operand: state.operand}] = struct{}{} +func resultLocation(fn llvm.Value, result int) location { + return location{value: fn, result: result} +} + +func (g *locationGraph) add(from location, edge locationEdge) { + edges := g.edges[from] + if edges == nil { + edges = make(map[locationEdge]struct{}) + g.edges[from] = edges + } + edges[edge] = struct{}{} } func (g *locationGraph) addFlow(from, to llvm.Value) { - g.edges[locationEdge{from: from, to: to, operand: -1}] = struct{}{} + g.add(valueLocation(from), locationEdge{to: valueLocation(to), kind: locationFlow}) +} + +func (g *locationGraph) addDeref(from, to llvm.Value) { + g.add(valueLocation(from), locationEdge{to: valueLocation(to), kind: locationDeref}) +} + +func (g *locationGraph) addHeap(from llvm.Value) { + g.add(valueLocation(from), locationEdge{kind: locationHeap}) +} + +func (g *locationGraph) addMutator(from llvm.Value) { + g.add(valueLocation(from), locationEdge{kind: locationMutator}) +} + +func (g *locationGraph) addCallee(from llvm.Value) { + g.add(valueLocation(from), locationEdge{kind: locationCallee}) +} + +func (g *locationGraph) addResult(from, fn llvm.Value, result int) { + g.add(valueLocation(from), locationEdge{to: resultLocation(fn, result), kind: locationFlow}) +} + +func (g *locationGraph) addCallResult(fn llvm.Value, result int, call llvm.Value) { + g.add(resultLocation(fn, result), locationEdge{to: valueLocation(call), kind: locationFlow}) +} + +const ( + leakHeap = iota + leakMutator + leakCallee + leakResult0 + numEscResults = 5 +) + +type leaks [leakResult0 + numEscResults]uint8 + +func (l leaks) get(index int) int { + return int(l[index]) - 1 +} + +func (l *leaks) add(index, level int) { + if old := l.get(index); old >= 0 && old <= level { + return + } + value := level + 1 + if value > math.MaxUint8 { + value = math.MaxUint8 + } + l[index] = uint8(value) +} + +func (l *leaks) optimize() { + heap := l.get(leakHeap) + if heap < 0 { + return + } + for index := leakMutator; index < len(l); index++ { + if l.get(index) >= heap { + l[index] = 0 + } + } +} + +func (g *locationGraph) summarize(root paramKey) leaks { + var summary leaks + start := valueLocation(root.fn.Param(root.param)) + best := map[location]int{start: 0} + worklist := []location{start} + + for len(worklist) != 0 { + current := worklist[len(worklist)-1] + worklist = worklist[:len(worklist)-1] + level := best[current] + for edge := range g.edges[current] { + switch edge.kind { + case locationFlow, locationDeref: + nextLevel := level + if edge.kind == locationDeref { + nextLevel++ + } + if edge.to.result >= 0 && edge.to.value == root.fn { + if edge.to.result < numEscResults { + summary.add(leakResult0+edge.to.result, nextLevel) + } + continue + } + if old, ok := best[edge.to]; !ok || nextLevel < old { + best[edge.to] = nextLevel + worklist = append(worklist, edge.to) + } + case locationHeap: + summary.add(leakHeap, level) + case locationMutator: + summary.add(leakMutator, level) + case locationCallee: + summary.add(leakCallee, level) + } + } + } + summary.optimize() + return summary } type analyzer struct { noCapture map[paramKey]bool noCaptureKeys []paramKey - locations locationGraph + locations *locationGraph + copies *copyAnalysis } -func newAnalyzer(mod llvm.Module) *analyzer { +func newAnalyzer(mod llvm.Module, diagnostics bool) *analyzer { a := &analyzer{ noCapture: make(map[paramKey]bool), - locations: newLocationGraph(), + copies: newCopyAnalysis(mod), + } + if diagnostics { + locations := newLocationGraph() + a.locations = &locations } for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { if fn.IsDeclaration() || isRuntimeFunction(fn) { @@ -117,7 +252,6 @@ func (a *analyzer) addUses(worklist *[]useState, value llvm.Value) { if user.Operand(operand) == value { state := useState{user: user, operand: operand} *worklist = append(*worklist, state) - a.locations.addUse(value, state) } } } @@ -146,10 +280,9 @@ func (a *analyzer) checkForAllUses(root llvm.Value, classify func(useState) useA visited[state] = struct{}{} if state.user.InstructionOpcode() == llvm.Store && state.operand == 0 { - copies, ok := exactLocalCopies(state.user) + copies, ok := a.copies.getPotentialCopiesOfStoredValue(state.user) if ok { for _, copy := range copies { - a.locations.addFlow(state.user.Operand(0), copy) a.addUses(&worklist, copy) } continue @@ -170,37 +303,6 @@ func (a *analyzer) parameterNoCapture(key paramKey) bool { return a.checkForAllUses(key.fn.Param(key.param), a.classifyNoCaptureUse) } -func exactLocalCopies(store llvm.Value) ([]llvm.Value, bool) { - if store.IsVolatile() || store.Ordering() != llvm.AtomicOrderingNotAtomic { - return nil, false - } - slot := store.Operand(1).IsAAllocaInst() - if slot.IsNil() { - return nil, false - } - - stores := 0 - var copies []llvm.Value - for use := slot.FirstUse(); !use.IsNil(); use = use.NextUse() { - user := use.User() - switch { - case !user.IsAStoreInst().IsNil() && user.Operand(1) == slot: - stores++ - if user != store || user.IsVolatile() || user.Ordering() != llvm.AtomicOrderingNotAtomic { - return nil, false - } - case !user.IsALoadInst().IsNil() && user.Operand(0) == slot: - if user.IsVolatile() || user.Ordering() != llvm.AtomicOrderingNotAtomic || !isPointer(user.Type()) { - return nil, false - } - copies = append(copies, user) - default: - return nil, false - } - } - return copies, stores == 1 -} - func callCalleeOperand(call llvm.Value) int { called := call.CalledValue() index := -1 @@ -397,10 +499,164 @@ func (a *analyzer) callArgumentNoCapture(state useState) bool { return false } - a.locations.addFlow(call.Operand(state.operand), key.fn.Param(key.param)) return a.noCapture[key] } +func (a *analyzer) recordLocations() { + for _, key := range a.noCaptureKeys { + a.recordParameterLocations(key) + } +} + +func (a *analyzer) recordParameterLocations(root paramKey) { + worklist := make([]useState, 0, 8) + a.addUses(&worklist, root.fn.Param(root.param)) + visited := make(map[useState]struct{}) + + for len(worklist) != 0 { + state := worklist[len(worklist)-1] + worklist = worklist[:len(worklist)-1] + if _, ok := visited[state]; ok { + continue + } + visited[state] = struct{}{} + from := state.user.Operand(state.operand) + + if state.user.InstructionOpcode() == llvm.Store && state.operand == 0 { + copies, ok := a.copies.getPotentialCopiesOfStoredValue(state.user) + if ok { + for _, copy := range copies { + a.locations.addFlow(from, copy) + a.addUses(&worklist, copy) + } + continue + } + } + + user := state.user + switch user.InstructionOpcode() { + case llvm.Load: + if state.operand == 0 && isPointer(user.Type()) { + a.locations.addDeref(from, user) + a.addUses(&worklist, user) + } + case llvm.Store: + if state.operand == 0 { + a.locations.addHeap(from) + } else if state.operand == 1 { + a.locations.addMutator(from) + } + case llvm.Call, llvm.Invoke: + calleeOperand := callCalleeOperand(user) + if state.operand == calleeOperand { + a.locations.addCallee(from) + continue + } + if callee, ok := definedCallParameter(user, state.operand); ok { + a.locations.addFlow(from, callee.fn.Param(callee.param)) + if isPointer(user.Type()) { + a.locations.addCallResult(callee.fn, 0, user) + a.addUses(&worklist, user) + } + continue + } + fn := user.CalledValue().IsAFunction() + if fn.IsNil() || fn.IntrinsicID() == 0 || !intrinsicArgumentNoCapture(user, fn, state.operand) { + a.locations.addHeap(from) + } + case llvm.Ret: + a.locations.addResult(from, root.fn, 0) + case llvm.GetElementPtr: + if state.operand == 0 && user.Type().TypeKind() != llvm.VectorTypeKind { + a.locations.addFlow(from, user) + a.addUses(&worklist, user) + } else { + a.locations.addHeap(from) + } + case llvm.BitCast, llvm.PHI, opcodeAddrSpaceCast: + a.locations.addFlow(from, user) + a.addUses(&worklist, user) + case llvm.Select: + if state.operand == 1 || state.operand == 2 { + a.locations.addFlow(from, user) + a.addUses(&worklist, user) + } else { + a.locations.addHeap(from) + } + case opcodeFreeze: + if state.operand == 0 { + a.locations.addFlow(from, user) + a.addUses(&worklist, user) + } else { + a.locations.addHeap(from) + } + case opcodeAtomicRMW, opcodeAtomicCmpXchg: + if state.operand == 0 { + a.locations.addMutator(from) + } else { + a.locations.addHeap(from) + } + case llvm.VAArg: + default: + a.locations.addHeap(from) + } + } +} + +// Result contains diagnostic facts computed independently from heap-to-stack +// decisions. +type Result struct { + Parameters []ParameterSummary +} + +// ParameterSummary contains the Go-compatible leak summary for one LLVM formal +// parameter. +type ParameterSummary struct { + Function string + Parameter int + HeapLevel int + MutatorLevel int + CalleeLevel int + Results []ResultLeak +} + +// ResultLeak describes a parameter flow to one direct LLVM result. +type ResultLeak struct { + Result int + Level int +} + +func (a *analyzer) result() Result { + if a.locations == nil { + return Result{} + } + result := Result{Parameters: make([]ParameterSummary, 0, len(a.noCaptureKeys))} + for _, key := range a.noCaptureKeys { + leaks := a.locations.summarize(key) + summary := ParameterSummary{ + Function: key.fn.Name(), + Parameter: key.param, + HeapLevel: leaks.get(leakHeap), + MutatorLevel: leaks.get(leakMutator), + CalleeLevel: leaks.get(leakCallee), + } + for resultIndex := 0; resultIndex < numEscResults; resultIndex++ { + if level := leaks.get(leakResult0 + resultIndex); level >= 0 { + summary.Results = append(summary.Results, ResultLeak{Result: resultIndex, Level: level}) + } + } + result.Parameters = append(result.Parameters, summary) + } + sort.Slice(result.Parameters, func(i, j int) bool { + left, right := result.Parameters[i], result.Parameters[j] + if left.Function != right.Function { + return left.Function < right.Function + } + return left.Parameter < right.Parameter + }) + return result +} + func intrinsicArgumentNoCapture(call, fn llvm.Value, argument int) bool { name := fn.Name() if argument < 0 { @@ -428,10 +684,16 @@ type allocationPlan struct { } // TransformModule analyzes eligible LLGo allocations and rewrites proven-local -// AllocZ and AllocU calls. -func TransformModule(mod llvm.Module) { - a := newAnalyzer(mod) +// AllocZ and AllocU calls. Diagnostic summaries are computed only when +// diagnostics is true. +func TransformModule(mod llvm.Module, diagnostics bool) Result { + a := newAnalyzer(mod, diagnostics) + defer a.copies.dispose() a.solveNoCapture() + if a.locations != nil { + a.recordLocations() + } + result := a.result() var plans []allocationPlan for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { @@ -451,6 +713,7 @@ func TransformModule(mod llvm.Module) { for _, plan := range plans { rewriteAllocation(mod.Context(), plan) } + return result } func (a *analyzer) planAllocation(instr llvm.Value) (allocationPlan, bool) { diff --git a/internal/escape/escape_test.go b/internal/escape/escape_test.go index c57a2f2179..d399f0013a 100644 --- a/internal/escape/escape_test.go +++ b/internal/escape/escape_test.go @@ -3,8 +3,10 @@ package escape import ( + "fmt" "os" "path/filepath" + "strings" "testing" "github.com/goplus/llgo/internal/littest" @@ -34,7 +36,7 @@ func TestTransformModule(t *testing.T) { t.Fatalf("parse %s: %v", input, err) } defer mod.Dispose() - TransformModule(mod) + result := TransformModule(mod, true) output := filepath.Join(dir, "out.txt") want, err := os.ReadFile(output) @@ -45,6 +47,31 @@ func TestTransformModule(t *testing.T) { if err := littest.Check(spec, mod.String()); err != nil { t.Fatalf("%v\n%s", err, mod.String()) } + + diagnostics := filepath.Join(dir, "diagnostics.txt") + wantDiagnostics, err := os.ReadFile(diagnostics) + if os.IsNotExist(err) { + return + } + if err != nil { + t.Fatal(err) + } + spec = littest.Spec{Path: diagnostics, Text: string(wantDiagnostics), Mode: littest.ModeLiteral} + if got := formatParameterSummaries(result); littest.Check(spec, got) != nil { + t.Fatalf("diagnostics mismatch:\n%s", got) + } }) } } + +func formatParameterSummaries(result Result) string { + var out strings.Builder + for _, param := range result.Parameters { + fmt.Fprintf(&out, "%s param=%d heap=%d mutator=%d callee=%d", param.Function, param.Parameter, param.HeapLevel, param.MutatorLevel, param.CalleeLevel) + for _, leak := range param.Results { + fmt.Fprintf(&out, " result=%d level=%d", leak.Result, leak.Level) + } + out.WriteByte('\n') + } + return out.String() +} diff --git a/internal/escape/pointer_info.go b/internal/escape/pointer_info.go new file mode 100644 index 0000000000..598c2f36dd --- /dev/null +++ b/internal/escape/pointer_info.go @@ -0,0 +1,804 @@ +package escape + +import ( + "math" + "sort" + "strings" + + "github.com/xgo-dev/llvm" +) + +const ( + rangeUnassigned = int64(math.MinInt32) + rangeUnknown = int64(math.MaxInt32) +) + +type memoryRange struct { + offset int64 + size int64 +} + +func unknownMemoryRange() memoryRange { + return memoryRange{offset: rangeUnknown, size: rangeUnknown} +} + +func (r memoryRange) isUnassigned() bool { + return r.offset == rangeUnassigned && r.size == rangeUnassigned +} + +func (r memoryRange) isUnknown() bool { + return r.offset == rangeUnknown || r.size == rangeUnknown +} + +func (r memoryRange) mayOverlap(other memoryRange) bool { + if r.isUnknown() || other.isUnknown() { + return true + } + return other.offset+other.size > r.offset && other.offset < r.offset+r.size +} + +func (r *memoryRange) merge(other memoryRange) { + if other.isUnassigned() { + return + } + if r.isUnassigned() { + *r = other + return + } + if r.offset == rangeUnknown || other.offset == rangeUnknown { + r.offset = rangeUnknown + } + if r.size == rangeUnknown || other.size == rangeUnknown { + r.size = rangeUnknown + } + if r.offset == rangeUnknown && r.size == rangeUnknown { + return + } + if r.offset == rangeUnknown { + if other.size > r.size { + r.size = other.size + } + return + } + if r.size == rangeUnknown { + if other.offset < r.offset { + r.offset = other.offset + } + return + } + r.offset = min(r.offset, other.offset) + r.size = max(r.offset+r.size, other.offset+other.size) - r.offset +} + +type accessKind uint8 + +const ( + accessMust accessKind = 1 << iota + accessMay + accessRead + accessWrite +) + +type memoryAccess struct { + local llvm.Value + remote llvm.Value + ranges []memoryRange + kind accessKind +} + +func (a memoryAccess) isRead() bool { return a.kind&accessRead != 0 } +func (a memoryAccess) isWrite() bool { return a.kind&accessWrite != 0 } +func (a memoryAccess) isMust() bool { return a.kind&accessMust != 0 } + +type offsetInfo struct { + offsets []int64 +} + +func unknownOffsetInfo() offsetInfo { + return offsetInfo{offsets: []int64{rangeUnknown}} +} + +func (o offsetInfo) isUnknown() bool { + return len(o.offsets) == 1 && o.offsets[0] == rangeUnknown +} + +func (o offsetInfo) equal(other offsetInfo) bool { + if len(o.offsets) != len(other.offsets) { + return false + } + for index := range o.offsets { + if o.offsets[index] != other.offsets[index] { + return false + } + } + return true +} + +func (o *offsetInfo) merge(other offsetInfo) bool { + if o.isUnknown() { + return false + } + if other.isUnknown() { + *o = unknownOffsetInfo() + return true + } + seen := make(map[int64]struct{}, len(o.offsets)+len(other.offsets)) + for _, offset := range o.offsets { + seen[offset] = struct{}{} + } + changed := false + for _, offset := range other.offsets { + if _, ok := seen[offset]; ok { + continue + } + seen[offset] = struct{}{} + o.offsets = append(o.offsets, offset) + changed = true + } + if changed { + sort.Slice(o.offsets, func(i, j int) bool { return o.offsets[i] < o.offsets[j] }) + } + return changed +} + +type pointerInfo struct { + valid bool + accesses []memoryAccess +} + +type pointerInfoState uint8 + +const ( + pointerInfoUnseen pointerInfoState = iota + pointerInfoBuilding + pointerInfoReady +) + +type copyAnalysis struct { + mod llvm.Module + td llvm.TargetData + infos map[llvm.Value]*pointerInfo + states map[llvm.Value]pointerInfoState + callSites map[llvm.Value][]llvm.Value +} + +func newCopyAnalysis(mod llvm.Module) *copyAnalysis { + a := ©Analysis{ + mod: mod, + td: llvm.NewTargetData(mod.DataLayout()), + infos: make(map[llvm.Value]*pointerInfo), + states: make(map[llvm.Value]pointerInfoState), + callSites: make(map[llvm.Value][]llvm.Value), + } + for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { + if fn.IsDeclaration() { + continue + } + for block := fn.FirstBasicBlock(); !block.IsNil(); block = llvm.NextBasicBlock(block) { + for instr := block.FirstInstruction(); !instr.IsNil(); instr = llvm.NextInstruction(instr) { + if call := instr.IsACallInst(); !call.IsNil() { + if callee := call.CalledValue().IsAFunction(); !callee.IsNil() { + a.callSites[callee] = append(a.callSites[callee], call) + } + } + } + } + } + return a +} + +func (a *copyAnalysis) dispose() { + a.td.Dispose() +} + +func (a *copyAnalysis) pointerInfo(root llvm.Value) (*pointerInfo, bool) { + switch a.states[root] { + case pointerInfoReady: + info := a.infos[root] + return info, info.valid + case pointerInfoBuilding: + return nil, false + } + + info := &pointerInfo{valid: true} + a.infos[root] = info + a.states[root] = pointerInfoBuilding + info.valid = a.buildPointerInfo(root, info) + a.states[root] = pointerInfoReady + return info, info.valid +} + +func (a *copyAnalysis) buildPointerInfo(root llvm.Value, info *pointerInfo) bool { + offsets := map[llvm.Value]offsetInfo{root: {offsets: []int64{0}}} + worklist := []llvm.Value{root} + inWorklist := map[llvm.Value]bool{root: true} + + enqueue := func(value llvm.Value, incoming offsetInfo) { + current, ok := offsets[value] + if ok && !current.merge(incoming) { + return + } + if !ok { + current = offsetInfo{} + current.merge(incoming) + offsets[value] = current + } else { + offsets[value] = current + } + if !inWorklist[value] { + worklist = append(worklist, value) + inWorklist[value] = true + } + } + + for len(worklist) != 0 { + value := worklist[len(worklist)-1] + worklist = worklist[:len(worklist)-1] + inWorklist[value] = false + valueOffsets := offsets[value] + + for use := value.FirstUse(); !use.IsNil(); use = use.NextUse() { + user := use.User() + for operand := 0; operand < user.OperandsCount(); operand++ { + if user.Operand(operand) != value { + continue + } + + if user.InstructionOpcode() == llvm.Store && operand == 0 { + copies, ok := a.getPotentialCopiesOfStoredValue(user) + if !ok { + return false + } + for _, copy := range copies { + enqueue(copy, valueOffsets) + } + continue + } + + switch user.InstructionOpcode() { + case llvm.GetElementPtr: + if operand != 0 || user.Type().TypeKind() == llvm.VectorTypeKind { + return false + } + enqueue(user, a.gepOffsets(user, valueOffsets)) + case llvm.BitCast, opcodeAddrSpaceCast: + enqueue(user, valueOffsets) + case llvm.Select: + if operand != 1 && operand != 2 { + return false + } + enqueue(user, valueOffsets) + case llvm.PHI: + current, exists := offsets[user] + if exists && blockInCycle(user.InstructionParent()) && !current.equal(valueOffsets) { + enqueue(user, unknownOffsetInfo()) + } else { + enqueue(user, valueOffsets) + } + case llvm.Ret: + fn := user.InstructionParent().Parent() + for _, call := range a.callSites[fn] { + enqueue(call, valueOffsets) + } + case llvm.Load: + kind := accessRead | a.accessCertainty(root, value) + a.addAccess(info, user, user, valueOffsets, int64(a.td.TypeStoreSize(user.Type())), kind) + case llvm.Store: + if operand != 1 { + return false + } + kind := accessWrite | a.accessCertainty(root, value) + a.addAccess(info, user, user, valueOffsets, int64(a.td.TypeStoreSize(user.Operand(0).Type())), kind) + case opcodeAtomicRMW, opcodeAtomicCmpXchg: + if operand != 0 { + return false + } + kind := accessRead | accessWrite | a.accessCertainty(root, value) + a.addAccess(info, user, user, valueOffsets, int64(a.td.TypeStoreSize(user.Operand(1).Type())), kind) + case llvm.Call, llvm.Invoke: + if !a.addCallAccesses(info, root, value, valueOffsets, user, operand) { + return false + } + case llvm.VAArg: + case opcodeFreeze: + if operand != 0 { + return false + } + enqueue(user, valueOffsets) + default: + return false + } + } + } + } + return true +} + +func (a *copyAnalysis) accessCertainty(root, pointer llvm.Value) accessKind { + objects := a.underlyingObjects(pointer) + if len(objects) == 1 && objects[0] == root { + return accessMust + } + return accessMay +} + +func (a *copyAnalysis) addAccess(info *pointerInfo, local, remote llvm.Value, offsets offsetInfo, size int64, kind accessKind) { + ranges := make([]memoryRange, 0, len(offsets.offsets)) + if offsets.isUnknown() || size == rangeUnknown { + ranges = append(ranges, unknownMemoryRange()) + } else { + for _, offset := range offsets.offsets { + ranges = append(ranges, memoryRange{offset: offset, size: size}) + } + } + if len(ranges) != 1 { + kind &^= accessMust + kind |= accessMay + } + for index := range info.accesses { + access := &info.accesses[index] + if access.local != local || access.remote != remote { + continue + } + for _, candidate := range ranges { + found := false + for rangeIndex := range access.ranges { + if access.ranges[rangeIndex].offset == candidate.offset { + access.ranges[rangeIndex].merge(candidate) + found = true + break + } + } + if !found { + access.ranges = append(access.ranges, candidate) + } + } + if len(access.ranges) != 1 { + access.kind &^= accessMust + access.kind |= accessMay + } + access.kind |= kind + if access.kind&accessMay != 0 { + access.kind &^= accessMust + } + return + } + info.accesses = append(info.accesses, memoryAccess{local: local, remote: remote, ranges: ranges, kind: kind}) +} + +func (a *copyAnalysis) gepOffsets(gep llvm.Value, base offsetInfo) offsetInfo { + if base.isUnknown() { + return unknownOffsetInfo() + } + current := append([]int64(nil), base.offsets...) + typ := gep.GEPSourceElementType() + for operand := 1; operand < gep.OperandsCount(); operand++ { + indices, ok := a.potentialConstants(gep.Operand(operand), make(map[llvm.Value]bool)) + if !ok || len(indices) == 0 { + return unknownOffsetInfo() + } + var additions []int64 + if operand == 1 { + scale := int64(a.td.TypeAllocSize(typ)) + for _, index := range indices { + additions = append(additions, index*scale) + } + } else { + switch typ.TypeKind() { + case llvm.StructTypeKind: + if len(indices) != 1 || indices[0] < 0 || indices[0] >= int64(typ.StructElementTypesCount()) { + return unknownOffsetInfo() + } + field := int(indices[0]) + additions = append(additions, int64(a.td.ElementOffset(typ, field))) + typ = typ.StructElementTypes()[field] + current = addOffsetProduct(current, additions) + continue + case llvm.ArrayTypeKind, llvm.VectorTypeKind: + elem := typ.ElementType() + scale := int64(a.td.TypeAllocSize(elem)) + for _, index := range indices { + additions = append(additions, index*scale) + } + typ = elem + default: + return unknownOffsetInfo() + } + } + current = addOffsetProduct(current, additions) + } + return offsetInfo{offsets: uniqueSortedOffsets(current)} +} + +func addOffsetProduct(base, additions []int64) []int64 { + result := make([]int64, 0, len(base)*len(additions)) + for _, value := range base { + for _, addition := range additions { + result = append(result, value+addition) + } + } + return result +} + +func uniqueSortedOffsets(offsets []int64) []int64 { + sort.Slice(offsets, func(i, j int) bool { return offsets[i] < offsets[j] }) + result := offsets[:0] + for _, offset := range offsets { + if len(result) == 0 || result[len(result)-1] != offset { + result = append(result, offset) + } + } + return result +} + +func (a *copyAnalysis) potentialConstants(value llvm.Value, visiting map[llvm.Value]bool) ([]int64, bool) { + if constant := value.IsAConstantInt(); !constant.IsNil() { + return []int64{constant.SExtValue()}, true + } + if visiting[value] { + return nil, false + } + visiting[value] = true + defer delete(visiting, value) + + var values []int64 + switch value.InstructionOpcode() { + case llvm.Select: + for operand := 1; operand <= 2; operand++ { + set, ok := a.potentialConstants(value.Operand(operand), visiting) + if !ok { + return nil, false + } + values = append(values, set...) + } + case llvm.PHI: + for index := 0; index < value.IncomingCount(); index++ { + set, ok := a.potentialConstants(value.IncomingValue(index), visiting) + if !ok { + return nil, false + } + values = append(values, set...) + } + default: + return nil, false + } + return uniqueSortedOffsets(values), true +} + +func (a *copyAnalysis) underlyingObjects(value llvm.Value) []llvm.Value { + objects := make(map[llvm.Value]struct{}) + var visit func(llvm.Value, map[llvm.Value]bool) + visit = func(current llvm.Value, path map[llvm.Value]bool) { + if path[current] { + return + } + nextPath := make(map[llvm.Value]bool, len(path)+1) + for seen := range path { + nextPath[seen] = true + } + nextPath[current] = true + + switch current.InstructionOpcode() { + case llvm.GetElementPtr, llvm.BitCast, opcodeAddrSpaceCast: + visit(current.Operand(0), nextPath) + case llvm.Select: + visit(current.Operand(1), nextPath) + visit(current.Operand(2), nextPath) + case llvm.PHI: + for index := 0; index < current.IncomingCount(); index++ { + visit(current.IncomingValue(index), nextPath) + } + default: + objects[current] = struct{}{} + } + } + visit(value, make(map[llvm.Value]bool)) + result := make([]llvm.Value, 0, len(objects)) + for object := range objects { + result = append(result, object) + } + return result +} + +func (a *copyAnalysis) addCallAccesses(info *pointerInfo, root, pointer llvm.Value, offsets offsetInfo, call llvm.Value, operand int) bool { + calleeOperand := callCalleeOperand(call) + if operand == calleeOperand { + return true + } + if strings.HasPrefix(call.CalledValue().Name(), "llvm.lifetime.") { + return true + } + if !call.IsAMemCpyInst().IsNil() || !call.IsAMemMoveInst().IsNil() || !call.IsAMemSetInst().IsNil() { + if operand > 1 { + return true + } + size := int64(rangeUnknown) + if call.OperandsCount() > 2 { + if length := call.Operand(2).IsAConstantInt(); !length.IsNil() { + size = int64(length.SExtValue()) + } + } + kind := accessMust | accessWrite + if operand == 1 { + kind = accessMust | accessRead + } + a.addAccess(info, call, call, offsets, size, kind) + return true + } + + callee := call.CalledValue().IsAFunction() + if callee.IsNil() || operand >= callee.ParamsCount() { + return false + } + if !callee.IsDeclaration() { + calleeInfo, ok := a.pointerInfo(callee.Param(operand)) + if !ok { + return false + } + for _, access := range calleeInfo.accesses { + translated := access + translated.local = call + translated.ranges = translateRanges(access.ranges, offsets) + if len(translated.ranges) != 1 { + translated.kind &^= accessMust + translated.kind |= accessMay + } + info.accesses = append(info.accesses, translated) + } + return true + } + + if !callArgumentHasAttribute(call, callee, operand, "nocapture") { + return false + } + if callArgumentHasAttribute(call, callee, operand, "readnone") { + return true + } + kind := accessMay | accessRead | accessWrite + if callArgumentHasAttribute(call, callee, operand, "readonly") { + kind = accessMay | accessRead + } + a.addAccess(info, call, call, unknownOffsetInfo(), rangeUnknown, kind) + return true +} + +func translateRanges(ranges []memoryRange, offsets offsetInfo) []memoryRange { + if offsets.isUnknown() { + return []memoryRange{unknownMemoryRange()} + } + var result []memoryRange + for _, base := range offsets.offsets { + for _, current := range ranges { + if current.isUnknown() { + return []memoryRange{unknownMemoryRange()} + } + result = append(result, memoryRange{offset: base + current.offset, size: current.size}) + } + } + return result +} + +func callArgumentHasAttribute(call, callee llvm.Value, argument int, name string) bool { + kind := llvm.AttributeKindID(name) + index := argument + 1 + if attr := call.GetCallSiteEnumAttribute(index, kind); !attr.IsNil() { + return true + } + return !callee.GetEnumAttributeAtIndex(index, kind).IsNil() +} + +func (a *copyAnalysis) getPotentialCopiesOfStoredValue(store llvm.Value) ([]llvm.Value, bool) { + objects := a.underlyingObjects(store.Operand(1)) + potentialCopies := make(map[llvm.Value]struct{}) + + for _, object := range objects { + if !object.IsAUndefValue().IsNil() { + continue + } + if !object.IsAConstantPointerNull().IsNil() { + if store.Operand(1) == object && !functionNullPointerIsValid(store.InstructionParent().Parent()) { + continue + } + return nil, false + } + if !a.supportedUnderlyingObject(object) { + return nil, false + } + info, ok := a.pointerInfo(object) + if !ok { + return nil, false + } + copies, ok := a.interferingLoadCopies(store, object, info) + if !ok { + return nil, false + } + for _, copy := range copies { + potentialCopies[copy] = struct{}{} + } + } + + result := make([]llvm.Value, 0, len(potentialCopies)) + for copy := range potentialCopies { + result = append(result, copy) + } + return result, true +} + +func functionNullPointerIsValid(fn llvm.Value) bool { + return !fn.GetStringAttributeAtIndex(-1, "null-pointer-is-valid").IsNil() +} + +func (a *copyAnalysis) supportedUnderlyingObject(object llvm.Value) bool { + if !object.IsAAllocaInst().IsNil() { + return true + } + if global := object.IsAGlobalVariable(); !global.IsNil() { + linkage := global.Linkage() + return linkage == llvm.InternalLinkage || linkage == llvm.PrivateLinkage || global.IsGlobalConstant() && !global.Initializer().IsNil() + } + if call := object.IsACallInst(); !call.IsNil() { + callee := call.CalledValue().IsAFunction() + if callee.IsNil() { + return false + } + kind := llvm.AttributeKindID("noalias") + return !call.GetCallSiteEnumAttribute(0, kind).IsNil() || !callee.GetEnumAttributeAtIndex(0, kind).IsNil() + } + return false +} + +func (a *copyAnalysis) interferingLoadCopies(store, object llvm.Value, info *pointerInfo) ([]llvm.Value, bool) { + query := memoryRange{offset: rangeUnassigned, size: rangeUnassigned} + for _, access := range info.accesses { + if access.local != store && access.remote != store { + continue + } + for _, current := range access.ranges { + query.merge(current) + } + } + if query.isUnassigned() { + return nil, false + } + + blockers := make(map[llvm.Value]bool) + for _, access := range info.accesses { + if !access.isWrite() || !access.isMust() || access.remote == store { + continue + } + for _, current := range access.ranges { + if current == query && !query.isUnknown() { + blockers[access.remote] = true + } + } + } + + copies := make(map[llvm.Value]struct{}) + threadLocal := !object.IsAAllocaInst().IsNil() || object.IsThreadLocal() + for _, access := range info.accesses { + if !access.isRead() { + continue + } + for _, current := range access.ranges { + if !query.mayOverlap(current) { + continue + } + if threadLocal && !a.potentiallyReachable(store, access.remote, blockers) { + continue + } + if query.isUnknown() || current.isUnknown() || current != query { + return nil, false + } + if access.remote.IsALoadInst().IsNil() { + return nil, false + } + copies[access.remote] = struct{}{} + } + } + + result := make([]llvm.Value, 0, len(copies)) + for copy := range copies { + result = append(result, copy) + } + return result, true +} + +func (a *copyAnalysis) potentiallyReachable(from, to llvm.Value, blockers map[llvm.Value]bool) bool { + fromFn := from.InstructionParent().Parent() + toFn := to.InstructionParent().Parent() + if fromFn == toFn { + if intraFunctionReachable(from, to, blockers) { + return true + } + if !canReachFunctionReturn(from, blockers) { + return false + } + norecurse := llvm.AttributeKindID("norecurse") + return fromFn.GetEnumFunctionAttribute(norecurse).IsNil() + } + + worklist := []llvm.Value{from} + visited := make(map[llvm.Value]bool) + for len(worklist) != 0 { + current := worklist[len(worklist)-1] + worklist = worklist[:len(worklist)-1] + if current == to { + return true + } + if visited[current] || current != from && blockers[current] { + continue + } + visited[current] = true + worklist = append(worklist, a.instructionSuccessors(current)...) + } + return false +} + +func intraFunctionReachable(from, to llvm.Value, blockers map[llvm.Value]bool) bool { + worklist := []llvm.Value{from} + visited := make(map[llvm.Value]bool) + for len(worklist) != 0 { + current := worklist[len(worklist)-1] + worklist = worklist[:len(worklist)-1] + if current == to { + return true + } + if visited[current] || current != from && blockers[current] { + continue + } + visited[current] = true + if next := llvm.NextInstruction(current); !next.IsNil() { + worklist = append(worklist, next) + continue + } + terminator := current.InstructionParent().LastInstruction() + for index := 0; index < terminator.SuccessorsCount(); index++ { + if first := terminator.Successor(index).FirstInstruction(); !first.IsNil() { + worklist = append(worklist, first) + } + } + } + return false +} + +func canReachFunctionReturn(from llvm.Value, blockers map[llvm.Value]bool) bool { + fn := from.InstructionParent().Parent() + for block := fn.FirstBasicBlock(); !block.IsNil(); block = llvm.NextBasicBlock(block) { + terminator := block.LastInstruction() + if terminator.InstructionOpcode() == llvm.Ret && intraFunctionReachable(from, terminator, blockers) { + return true + } + } + return false +} + +func (a *copyAnalysis) instructionSuccessors(instr llvm.Value) []llvm.Value { + var result []llvm.Value + if next := llvm.NextInstruction(instr); !next.IsNil() { + result = append(result, next) + } else { + terminator := instr.InstructionParent().LastInstruction() + for index := 0; index < terminator.SuccessorsCount(); index++ { + if first := terminator.Successor(index).FirstInstruction(); !first.IsNil() { + result = append(result, first) + } + } + } + + if call := instr.IsACallInst(); !call.IsNil() { + if callee := call.CalledValue().IsAFunction(); !callee.IsNil() && !callee.IsDeclaration() { + if first := callee.EntryBasicBlock().FirstInstruction(); !first.IsNil() { + result = append(result, first) + } + } + } + if instr.InstructionOpcode() == llvm.Ret { + fn := instr.InstructionParent().Parent() + for _, call := range a.callSites[fn] { + if next := llvm.NextInstruction(call); !next.IsNil() { + result = append(result, next) + } + } + } + return result +} diff --git a/internal/escape/testdata/diagnostics/diagnostics.txt b/internal/escape/testdata/diagnostics/diagnostics.txt new file mode 100644 index 0000000000..8ffaa7e766 --- /dev/null +++ b/internal/escape/testdata/diagnostics/diagnostics.txt @@ -0,0 +1,12 @@ +call_parameter param=0 heap=-1 mutator=-1 callee=0 +content_result param=0 heap=-1 mutator=-1 callee=-1 result=0 level=1 +discard_result param=0 heap=-1 mutator=-1 callee=-1 +identity param=0 heap=-1 mutator=-1 callee=-1 result=0 level=0 +mutate param=0 heap=-1 mutator=0 callee=-1 +summaries param=0 heap=-1 mutator=-1 callee=-1 +summaries param=1 heap=0 mutator=-1 callee=-1 +summaries param=2 heap=1 mutator=-1 callee=-1 +summaries param=3 heap=-1 mutator=-1 callee=-1 result=0 level=0 +summaries param=4 heap=0 mutator=-1 callee=-1 +through_content_result param=0 heap=-1 mutator=-1 callee=-1 result=0 level=1 +through_result param=0 heap=-1 mutator=-1 callee=-1 result=0 level=0 diff --git a/internal/escape/testdata/diagnostics/in.txt b/internal/escape/testdata/diagnostics/in.txt new file mode 100644 index 0000000000..0a8934f29b --- /dev/null +++ b/internal/escape/testdata/diagnostics/in.txt @@ -0,0 +1,54 @@ +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +@escaped = global ptr null + +define ptr @summaries(ptr %safe, ptr %heap, ptr %content, ptr %result, ptr %mixed) { +entry: + %value = load i8, ptr %safe + store ptr %heap, ptr @escaped + %loaded = load ptr, ptr %content + store ptr %loaded, ptr @escaped + store ptr %mixed, ptr @escaped + ret ptr %result +} + +define ptr @content_result(ptr %p) { +entry: + %loaded = load ptr, ptr %p + ret ptr %loaded +} + +define ptr @identity(ptr %p) { +entry: + ret ptr %p +} + +define ptr @through_result(ptr %p) { +entry: + %result = call ptr @identity(ptr %p) + ret ptr %result +} + +define ptr @through_content_result(ptr %p) { +entry: + %result = call ptr @content_result(ptr %p) + ret ptr %result +} + +define void @discard_result(ptr %p) { +entry: + %result = call ptr @identity(ptr %p) + ret void +} + +define void @mutate(ptr %p) { +entry: + store i8 1, ptr %p + ret void +} + +define void @call_parameter(ptr %fn) { +entry: + call void %fn() + ret void +} diff --git a/internal/escape/testdata/diagnostics/out.txt b/internal/escape/testdata/diagnostics/out.txt new file mode 100644 index 0000000000..6b004c4009 --- /dev/null +++ b/internal/escape/testdata/diagnostics/out.txt @@ -0,0 +1,56 @@ +; ModuleID = 'testdata/diagnostics/in.txt' +source_filename = "testdata/diagnostics/in.txt" +target datalayout = "e-p:64:64-i64:64-n32:64-S128" + +@escaped = global ptr null + +define ptr @summaries(ptr %safe, ptr %heap, ptr %content, ptr %result, ptr %mixed) { +entry: + %value = load i8, ptr %safe, align 1 + store ptr %heap, ptr @escaped, align 8 + %loaded = load ptr, ptr %content, align 8 + store ptr %loaded, ptr @escaped, align 8 + store ptr %mixed, ptr @escaped, align 8 + ret ptr %result +} + +define ptr @content_result(ptr %p) { +entry: + %loaded = load ptr, ptr %p, align 8 + ret ptr %loaded +} + +define ptr @identity(ptr %p) { +entry: + ret ptr %p +} + +define ptr @through_result(ptr %p) { +entry: + %result = call ptr @identity(ptr %p) + ret ptr %result +} + +define ptr @through_content_result(ptr %p) { +entry: + %result = call ptr @content_result(ptr %p) + ret ptr %result +} + +define void @discard_result(ptr %p) { +entry: + %result = call ptr @identity(ptr %p) + ret void +} + +define void @mutate(ptr %p) { +entry: + store i8 1, ptr %p, align 1 + ret void +} + +define void @call_parameter(ptr %fn) { +entry: + call void %fn() + ret void +} diff --git a/internal/escape/testdata/exact-copy/in.txt b/internal/escape/testdata/exact-copy/in.txt index 5afbdddb6e..e7f85def06 100644 --- a/internal/escape/testdata/exact-copy/in.txt +++ b/internal/escape/testdata/exact-copy/in.txt @@ -1,9 +1,21 @@ target datalayout = "e-p:64:64-i64:64-n32:64-S128" +%Pair = type { i32, ptr } + @escaped = global ptr null +@local_slot = internal global ptr null declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) declare void @clobber(ptr) +declare noalias ptr @slot_alloc(i64) +declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1 immarg) + +define void @read_slot(ptr %slot) { +entry: + %p = load ptr, ptr %slot + store i8 1, ptr %p + ret void +} define void @exact_copy() { entry: @@ -75,3 +87,160 @@ entry: store ptr %p, ptr %dst ret void } + +define void @struct_field() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %object = alloca %Pair + %field1 = getelementptr %Pair, ptr %object, i32 0, i32 1 + store ptr %p, ptr %field1 + %field2 = getelementptr %Pair, ptr %object, i32 0, i32 1 + %q = load ptr, ptr %field2 + store i8 1, ptr %q + ret void +} + +define void @array_element() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %array = alloca [2 x ptr] + %element1 = getelementptr [2 x ptr], ptr %array, i32 0, i32 1 + store ptr %p, ptr %element1 + %element2 = getelementptr [2 x ptr], ptr %array, i32 0, i32 1 + %q = load ptr, ptr %element2 + store i8 1, ptr %q + ret void +} + +define void @whole_struct_reader() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %object = alloca %Pair + %field = getelementptr %Pair, ptr %object, i32 0, i32 1 + store ptr %p, ptr %field + %whole = load %Pair, ptr %object + %q = extractvalue %Pair %whole, 1 + store i8 1, ptr %q + ret void +} + +define void @read_before_store() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr null, ptr %slot + %before = load ptr, ptr %slot + store ptr %before, ptr @escaped + store ptr %p, ptr %slot + ret void +} + +define void @overwritten_copy() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr %p, ptr %slot + store ptr null, ptr %slot + %q = load ptr, ptr %slot + store ptr %q, ptr @escaped + ret void +} + +define void @selected_destination(i1 %condition) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + %selected = select i1 %condition, ptr %slot, ptr %slot + store ptr %p, ptr %selected + %q = load ptr, ptr %slot + store i8 1, ptr %q + ret void +} + +define void @callee_reader() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr %p, ptr %slot + call void @read_slot(ptr %slot) + ret void +} + +define void @read_before_store_norecurse() norecurse { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr + store ptr null, ptr %slot + %before = load ptr, ptr %slot + store ptr %before, ptr @escaped + store ptr %p, ptr %slot + ret void +} + +define void @two_underlying_objects(i1 %condition) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot1 = alloca ptr + %slot2 = alloca ptr + %selected = select i1 %condition, ptr %slot1, ptr %slot2 + store ptr %p, ptr %selected + %q1 = load ptr, ptr %slot1 + %q2 = load ptr, ptr %slot2 + store i8 1, ptr %q1 + store i8 2, ptr %q2 + ret void +} + +define void @finite_dynamic_offset(i1 %condition) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %array = alloca [2 x ptr] + %index = select i1 %condition, i64 0, i64 1 + %element = getelementptr [2 x ptr], ptr %array, i64 0, i64 %index + store ptr %p, ptr %element + %q = load ptr, ptr %element + store i8 1, ptr %q + ret void +} + +define void @unknown_dynamic_offset(i64 %index) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %array = alloca [2 x ptr] + %element = getelementptr [2 x ptr], ptr %array, i64 0, i64 %index + store ptr %p, ptr %element + %q = load ptr, ptr %element + store i8 1, ptr %q + ret void +} + +define void @memcpy_reader() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %source = alloca ptr + %destination = alloca ptr + store ptr %p, ptr %source + call void @llvm.memcpy.p0.p0.i64(ptr %destination, ptr %source, i64 8, i1 false) + %q = load ptr, ptr %destination + store i8 1, ptr %q + ret void +} + +define void @internal_global_copy() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + store ptr %p, ptr @local_slot + %q = load ptr, ptr @local_slot + store i8 1, ptr %q + ret void +} + +define void @noalias_object_copy() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = call noalias ptr @slot_alloc(i64 8) + store ptr %p, ptr %slot + %q = load ptr, ptr %slot + store i8 1, ptr %q + ret void +} diff --git a/internal/escape/testdata/exact-copy/out.txt b/internal/escape/testdata/exact-copy/out.txt index 17b56e0a58..aa6394d109 100644 --- a/internal/escape/testdata/exact-copy/out.txt +++ b/internal/escape/testdata/exact-copy/out.txt @@ -2,12 +2,27 @@ source_filename = "testdata/exact-copy/in.txt" target datalayout = "e-p:64:64-i64:64-n32:64-S128" +%Pair = type { i32, ptr } + @escaped = global ptr null +@local_slot = internal global ptr null declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64) declare void @clobber(ptr) +declare noalias ptr @slot_alloc(i64) + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0 + +define void @read_slot(ptr %slot) { +entry: + %p = load ptr, ptr %slot, align 8 + store i8 1, ptr %p, align 1 + ret void +} + define void @exact_copy() { entry: %p.stack = alloca i8, i64 8, align 1 @@ -20,10 +35,10 @@ entry: define void @alias() { entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %p.stack = alloca i8, i64 8, align 1 %slot = alloca ptr, align 8 %alias = getelementptr ptr, ptr %slot, i64 0 - store ptr %p, ptr %slot, align 8 + store ptr %p.stack, ptr %slot, align 8 %q = load ptr, ptr %alias, align 8 store i8 1, ptr %q, align 1 ret void @@ -49,18 +64,18 @@ entry: define void @unsupported_reader() { entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %p.stack = alloca i8, i64 8, align 1 %slot = alloca ptr, align 8 - store ptr %p, ptr %slot, align 8 + store ptr %p.stack, ptr %slot, align 8 %q = load i64, ptr %slot, align 8 ret void } define void @other_store() { entry: - %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %p.stack = alloca i8, i64 8, align 1 %slot = alloca ptr, align 8 - store ptr %p, ptr %slot, align 8 + store ptr %p.stack, ptr %slot, align 8 store ptr null, ptr %slot, align 8 ret void } @@ -78,3 +93,164 @@ entry: store ptr %p, ptr %dst, align 8 ret void } + +define void @struct_field() { +entry: + %p.stack = alloca i8, i64 8, align 1 + %object = alloca %Pair, align 8 + %field1 = getelementptr %Pair, ptr %object, i32 0, i32 1 + store ptr %p.stack, ptr %field1, align 8 + %field2 = getelementptr %Pair, ptr %object, i32 0, i32 1 + %q = load ptr, ptr %field2, align 8 + store i8 1, ptr %q, align 1 + ret void +} + +define void @array_element() { +entry: + %p.stack = alloca i8, i64 8, align 1 + %array = alloca [2 x ptr], align 8 + %element1 = getelementptr [2 x ptr], ptr %array, i32 0, i32 1 + store ptr %p.stack, ptr %element1, align 8 + %element2 = getelementptr [2 x ptr], ptr %array, i32 0, i32 1 + %q = load ptr, ptr %element2, align 8 + store i8 1, ptr %q, align 1 + ret void +} + +define void @whole_struct_reader() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %object = alloca %Pair, align 8 + %field = getelementptr %Pair, ptr %object, i32 0, i32 1 + store ptr %p, ptr %field, align 8 + %whole = load %Pair, ptr %object, align 8 + %q = extractvalue %Pair %whole, 1 + store i8 1, ptr %q, align 1 + ret void +} + +define void @read_before_store() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %slot = alloca ptr, align 8 + store ptr null, ptr %slot, align 8 + %before = load ptr, ptr %slot, align 8 + store ptr %before, ptr @escaped, align 8 + store ptr %p, ptr %slot, align 8 + ret void +} + +define void @overwritten_copy() { +entry: + %p.stack = alloca i8, i64 8, align 1 + %slot = alloca ptr, align 8 + store ptr %p.stack, ptr %slot, align 8 + store ptr null, ptr %slot, align 8 + %q = load ptr, ptr %slot, align 8 + store ptr %q, ptr @escaped, align 8 + ret void +} + +define void @selected_destination(i1 %condition) { +entry: + %p.stack = alloca i8, i64 8, align 1 + %slot = alloca ptr, align 8 + %selected = select i1 %condition, ptr %slot, ptr %slot + store ptr %p.stack, ptr %selected, align 8 + %q = load ptr, ptr %slot, align 8 + store i8 1, ptr %q, align 1 + ret void +} + +define void @callee_reader() { +entry: + %p.stack = alloca i8, i64 8, align 1 + %slot = alloca ptr, align 8 + store ptr %p.stack, ptr %slot, align 8 + call void @read_slot(ptr %slot) + ret void +} + +; Function Attrs: norecurse +define void @read_before_store_norecurse() #1 { +entry: + %p.stack = alloca i8, i64 8, align 1 + %slot = alloca ptr, align 8 + store ptr null, ptr %slot, align 8 + %before = load ptr, ptr %slot, align 8 + store ptr %before, ptr @escaped, align 8 + store ptr %p.stack, ptr %slot, align 8 + ret void +} + +define void @two_underlying_objects(i1 %condition) { +entry: + %p.stack = alloca i8, i64 8, align 1 + %slot1 = alloca ptr, align 8 + %slot2 = alloca ptr, align 8 + %selected = select i1 %condition, ptr %slot1, ptr %slot2 + store ptr %p.stack, ptr %selected, align 8 + %q1 = load ptr, ptr %slot1, align 8 + %q2 = load ptr, ptr %slot2, align 8 + store i8 1, ptr %q1, align 1 + store i8 2, ptr %q2, align 1 + ret void +} + +define void @finite_dynamic_offset(i1 %condition) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %array = alloca [2 x ptr], align 8 + %index = select i1 %condition, i64 0, i64 1 + %element = getelementptr [2 x ptr], ptr %array, i64 0, i64 %index + store ptr %p, ptr %element, align 8 + %q = load ptr, ptr %element, align 8 + store i8 1, ptr %q, align 1 + ret void +} + +define void @unknown_dynamic_offset(i64 %index) { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %array = alloca [2 x ptr], align 8 + %element = getelementptr [2 x ptr], ptr %array, i64 0, i64 %index + store ptr %p, ptr %element, align 8 + %q = load ptr, ptr %element, align 8 + store i8 1, ptr %q, align 1 + ret void +} + +define void @memcpy_reader() { +entry: + %p = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 8) + %source = alloca ptr, align 8 + %destination = alloca ptr, align 8 + store ptr %p, ptr %source, align 8 + call void @llvm.memcpy.p0.p0.i64(ptr %destination, ptr %source, i64 8, i1 false) + %q = load ptr, ptr %destination, align 8 + store i8 1, ptr %q, align 1 + ret void +} + +define void @internal_global_copy() { +entry: + %p.stack = alloca i8, i64 8, align 1 + store ptr %p.stack, ptr @local_slot, align 8 + %q = load ptr, ptr @local_slot, align 8 + store i8 1, ptr %q, align 1 + ret void +} + +define void @noalias_object_copy() { +entry: + %p.stack = alloca i8, i64 8, align 1 + %slot = call noalias ptr @slot_alloc(i64 8) + store ptr %p.stack, ptr %slot, align 8 + %q = load ptr, ptr %slot, align 8 + store i8 1, ptr %q, align 1 + ret void +} + +attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +attributes #1 = { norecurse }