Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
394 changes: 342 additions & 52 deletions src/cmd/compile/internal/ssa/ssa2llvm.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/cmd/compile/internal/ssa/ssa2llvm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func TestLLVMBuiltinDeclarationKeepsCallSiteSignatures(t *testing.T) {
Type: llvm.FunctionType(result, nil, false),
ReturnType: result,
ResultCount: 1,
ReturnCount: 1,
ClosureContextIndex: -1,
}
}
Expand Down
114 changes: 111 additions & 3 deletions src/cmd/internal/testdir/llvm_abi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ func runLLVMAArch64ABIDifferentialTest(t *testing.T, gorootTestDir string) {
}
for _, needle := range [][]byte{
[]byte("define goabiinternal"),
[]byte(" goret("),
[]byte(` "goretindex"="`),
[]byte(`"go_results_tuple"`),
[]byte(`gc "goallc"`),
} {
Expand All @@ -155,6 +157,62 @@ func runLLVMAArch64ABIDifferentialTest(t *testing.T, gorootTestDir string) {
if bytes.Contains(ir, []byte(`"go-stack-growth-statepoint"`)) {
t.Fatal("GoALLC IR still contains the obsolete stack-growth attribute")
}
for _, tc := range []struct {
name string
required [][]byte
forbidden [][]byte
}{
{
name: "main.memoryOnlyPointerResult",
required: [][]byte{
[]byte(`ptr goret([2 x ptr]) align 8 "goretindex"="0" %.result0`),
[]byte(`getelementptr i8, ptr %.result0`),
[]byte(`store ptr %first`),
[]byte(`store ptr %second`),
},
forbidden: [][]byte{[]byte("alloca"), []byte("memmove")},
},
{
name: "main.initializedStackResult",
required: [][]byte{
[]byte(`ptr goret(ptr) align 8 "goretindex"="16" %.result16`),
[]byte(`store ptr %pointer, ptr %.result16`),
},
forbidden: [][]byte{[]byte("alloca"), []byte("memmove")},
},
{
name: "main.deferredNamedStackResult",
required: [][]byte{
[]byte(`ptr goret([2 x ptr]) align 8 "goretindex"="16" %.result16`),
[]byte(`getelementptr i8, ptr %.result16`),
},
forbidden: [][]byte{
[]byte("!goallc.defer_result"),
[]byte("memmove.p0.p0.i64(ptr align 8 %.result16"),
[]byte("%open.defer.result16"),
},
},
{
name: "main.escapedNamedStackResult",
required: [][]byte{
[]byte(`ptr goret([2 x ptr]) align 8 "goretindex"="16" %.result16`),
[]byte(`call goabiinternal ptr @runtime.mallocgcSmallScanNoHeaderSC`),
[]byte(`call void @llvm.memmove.p0.p0.i64(ptr align 8 %.result16`),
},
},
} {
body := llvmABIIRFunction(t, ir, tc.name)
for _, required := range tc.required {
if !bytes.Contains(body, required) {
t.Fatalf("%s does not contain %q\n%s", tc.name, required, body)
}
}
for _, forbidden := range tc.forbidden {
if bytes.Contains(body, forbidden) {
t.Fatalf("%s still contains %q\n%s", tc.name, forbidden, body)
}
}
}

opt := llvmToolPath(t, "opt", "GOALLC_OPT")
runLLVMABICommand(t, ir, opt, "-passes=verify", "-disable-output")
Expand Down Expand Up @@ -195,7 +253,8 @@ func runLLVMAArch64ABIDifferentialTest(t *testing.T, gorootTestDir string) {
`(?s)define goabiinternal \{ ptr, i64 \} @main\.liveScalarStackArgument.*?ptr byval\(ptr\) align 8 %pointer.*?load ptr, ptr %pointer.*?"gc-live"\(ptr %[[:alnum:]$._-]+\).*?gc\.relocate`,
`(?s)define goabiinternal \{ ptr, ptr, i64 \} @main\.livePointerSequenceStackArguments.*?ptr byval\(ptr\) align 8 %first.*?ptr byval\(ptr\) align 8 %second.*?load ptr, ptr %first.*?load ptr, ptr %second.*?"gc-live"\(ptr %[[:alnum:]$._-]+, ptr %[[:alnum:]$._-]+\).*?gc\.relocate`,
`(?s)define goabiinternal \{ ptr, ptr, i64 \} @main\.livePointerAggregateStackArgument.*?ptr byval\(%main\.pointerStackAggregate\) align 8 %value.*?load %main\.pointerStackAggregate, ptr %value.*?"gc-live"\(ptr %[[:alnum:]$._-]+, ptr %[[:alnum:]$._-]+\).*?gc\.relocate`,
`(?s)define goabiinternal \{ i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, ptr, ptr \} @main\.pointerAggregateBothOverflow.*?ptr byval\(%main\.pointerStackAggregate\) align 8 %value.*?load %main\.pointerStackAggregate, ptr %value.*?"gc-live"\(ptr %[[:alnum:]$._-]+, ptr %[[:alnum:]$._-]+\).*?gc\.relocate`,
`(?s)define goabiinternal void @main\.callMemoryOnlyPointerResult\(ptr %fn, ptr %first, ptr %second, ptr goret\(\[2 x ptr\]\) align 8 "goretindex"="0" %\.result0\).*?@llvm\.experimental\.gc\.statepoint.*?ptr goret\(\[2 x ptr\]\) align 8 "goretindex"="0" %[[:alnum:]$._-]+, ptr nest %fn.*?"gc-live"\(ptr %\.result0, ptr %[[:alnum:]$._-]+\).*?ret void`,
`(?s)define goabiinternal \{ i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64 \} @main\.pointerAggregateBothOverflow.*?ptr byval\(%main\.pointerStackAggregate\) align 8 %value.*?ptr goret\(ptr\) align 8 "goretindex"="16" %\.result16.*?ptr goret\(ptr\) align 8 "goretindex"="17" %\.result17.*?load %main\.pointerStackAggregate, ptr %value.*?"gc-live"\(ptr %[[:alnum:]$._-]+, ptr %\.result17, ptr %[[:alnum:]$._-]+, ptr %\.result16\).*?gc\.relocate`,
} {
if !regexp.MustCompile(pattern).Match(rewrittenIR) {
t.Fatalf("rewritten GoALLC ABI IR does not match %q", pattern)
Expand All @@ -205,6 +264,17 @@ func runLLVMAArch64ABIDifferentialTest(t *testing.T, gorootTestDir string) {
"liveScalarStackArgument", "livePointerSequenceStackArguments",
"livePointerAggregateStackArgument", "overflowResults",
"stackAggregateResult", "bothOverflow", "pointerAggregateBothOverflow")
checkLLVMABIStatepointGoRetAttrs(t, rewrittenIR, map[string][]string{
"memoryOnlyPointerResult": {"0"},
"overflowResults": {"16", "17"},
"initializedStackResult": {"16"},
"deferredNamedStackResult": {"16"},
"escapedNamedStackResult": {"16"},
"stackAggregateResult": {"15"},
"bothOverflow": {"16", "17"},
"pointerAggregateBothOverflow": {"16", "17"},
"stackResultsAfterGrowth": {"16"},
})
runLLVMABICommand(t, rewrittenIR, opt,
"-load-pass-plugin="+plugin, "-passes=verify", "-disable-output", "-")

Expand Down Expand Up @@ -314,6 +384,13 @@ func runLLVMAArch64ABIDifferentialTest(t *testing.T, gorootTestDir string) {
nativeStackMaps: []int32{-1, 0, -1},
goallcStackMaps: []int32{-1, 1, -1},
},
{
name: "memoryOnlyPointerResult", args: 32, pointerBits: []int{2, 3},
nativeArgsMaps: [][]int{{2, 3}, nil},
goallcArgsMaps: [][]int{{2, 3}, nil},
nativeStackMaps: []int32{-1, 0, -1},
goallcStackMaps: []int32{-1, 1, -1},
},
{
name: "overflowResults", args: 48, pointerBits: []int{2, 3, 4, 5},
nativeArgsMaps: [][]int{{2, 3, 4, 5}, nil},
Expand Down Expand Up @@ -601,6 +678,20 @@ func llvmABIMachineFunction(t *testing.T, machineIR []byte, name string) []byte
return body
}

func llvmABIIRFunction(t *testing.T, ir []byte, name string) []byte {
t.Helper()
start := regexp.MustCompile(`(?m)^define [^\n]*@` + regexp.QuoteMeta(name) + `\(`).FindIndex(ir)
if start == nil {
t.Fatalf("IR has no function %s", name)
}
body := ir[start[0]:]
if end := bytes.Index(body, []byte("\n}")); end >= 0 {
return body[:end+2]
}
t.Fatalf("IR definition for %s has no end", name)
return nil
}

func llvmABIPCTableRanges(symbol llvmABISymbol, kind string) []int32 {
for _, table := range symbol.Function.PCTables {
if table.Kind != kind {
Expand Down Expand Up @@ -797,6 +888,23 @@ func checkLLVMABIStatepointTupleAttrs(t *testing.T, ir []byte, callees ...string
}
}

func checkLLVMABIStatepointGoRetAttrs(t *testing.T, ir []byte, callees map[string][]string) {
t.Helper()
for callee, indexes := range callees {
call := regexp.MustCompile(`(?m)^.*@llvm\.experimental\.gc\.statepoint.*@main\.` +
regexp.QuoteMeta(callee) + `.*$`).Find(ir)
if call == nil {
t.Fatalf("rewritten IR has no statepoint call to main.%s", callee)
}
for _, index := range indexes {
pattern := `goret\([^)]*\) align [0-9]+ "goretindex"="` + regexp.QuoteMeta(index) + `"`
if !regexp.MustCompile(pattern).Match(call) {
t.Fatalf("statepoint call to main.%s lost goret index %s: %s", callee, index, call)
}
}
}
}

func checkLLVMABIAssembly(t *testing.T, native, goallc []byte) {
t.Helper()

Expand Down Expand Up @@ -839,8 +947,8 @@ func checkLLVMABIAssembly(t *testing.T, native, goallc []byte) {
`(?m)^main\.pointerAggregateBothOverflow:`,
`(?s)\bbl\s+main\.overflowResults.*?\bldp\s+x[0-9]+, x[0-9]+, \[sp, #8\]`,
`(?s)\bbl\s+main\.overflowResults.*?\b(?:mov|stp)\s+[^\n]*x15`,
`(?s)\bbl\s+main\.stackAggregateResult.*?\bldp\s+x[0-9]+, x[0-9]+, \[sp, #8\].*?\bmov\s+x[0-9]+, x15`,
`(?s)\bbl\s+main\.bothOverflow.*?\bldp\s+x[0-9]+, x[0-9]+, \[sp, #32\]`,
`(?s)\bbl\s+main\.stackAggregateResult.*?\b(?:ldp\s+x[0-9]+, x[0-9]+|ldur\s+q[0-9]+), \[sp, #8\].*?\bmov\s+x[0-9]+, x15`,
`(?s)\bbl\s+main\.bothOverflow.*?\bldr\s+x[0-9]+, \[sp, #32\].*?\bldr\s+x[0-9]+, \[sp, #40\]`,
`(?s)\bbl\s+main\.bothOverflow.*?\b(?:mov|stp)\s+[^\n]*x15`,
} {
if !regexp.MustCompile(pattern).Match(goallc) {
Expand Down
4 changes: 2 additions & 2 deletions src/cmd/internal/testdir/llvm_alloca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func runLLVMAllocaStatepointTest(t *testing.T, gorootTestDir string) {
t.Fatalf("alloca deopt records=%d, want %d\n%s", got, want, rewrittenFunction)
}
if got, want := bytes.Count(rewrittenFunction,
[]byte("i64 40, i64 8, i64 8, i64 5, i64 64, i64 1, i64 29")), 4; got != want {
[]byte("i64 40, i64 8, i64 8, i64 1, i64 5, i64 64, i64 1, i64 29")), 4; got != want {
t.Fatalf("alloca bitmap payloads=%d, want %d\n%s", got, want, rewrittenFunction)
}
if got, want := bytes.Count(rewrittenFunction, []byte(`"gc-live"(ptr `)), 4; got != want {
Expand Down Expand Up @@ -341,7 +341,7 @@ func runLLVMAllocaStatepointTest(t *testing.T, gorootTestDir string) {
}
}

// A matching direct gc-live alloca expands the deopt layout into the
// The explicit contents-live bit expands the deopt layout into the
// callsite's LocalsPointerMaps. This fixture is live at every ordinary
// statepoint, so it does not need the fallback function-level StackObject.
// Native Go emits the equivalent pointer fields with a different frame-bit
Expand Down
Loading