Skip to content
Merged
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
50 changes: 37 additions & 13 deletions src/cmd/internal/testdir/llvm_alloca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,29 @@ func runLLVMAllocaStatepointTest(t *testing.T, gorootTestDir string) {
"-filetype=null", "-o", "-", goallcIR)
rewrittenFunction := llvmAllocaIRFunction(t, rewrittenIR, "p.localAcrossSafepoints")

// Four static ordinary call sites each keep all four pointer leaves live.
// The canonical loads are marked so SelectionDAG must use the corresponding
// alloca subslot rather than silently allocating a separate spill.
if got, want := bytes.Count(rewrittenFunction,
[]byte("!llvm.statepoint.fixed_stack_home")), 16; got != want {
t.Fatalf("fixed-stack-home loads=%d, want %d\n%s", got, want, rewrittenFunction)
// Four ordinary call sites describe the same 40-byte alloca as memory in a
// deopt suffix. Its four pointer leaves are bits 0, 2, 3, and 4 (0b11101).
// They must not become SSA roots or be written back after a mutating callee.
if bytes.Contains(rewrittenFunction, []byte("llvm.statepoint.fixed_stack_home")) {
t.Fatalf("obsolete fixed-stack-home metadata survived\n%s", rewrittenFunction)
}
if got, want := bytes.Count(rewrittenFunction,
[]byte("@llvm.experimental.gc.statepoint")), 4; got != want {
t.Fatalf("ordinary statepoints=%d, want %d\n%s", got, want, rewrittenFunction)
}
if got, want := bytes.Count(rewrittenFunction, []byte(`"deopt"(`)), 4; got != want {
t.Fatalf("alloca deopt records=%d, want %d\n%s", got, want, rewrittenFunction)
}
if got, want := bytes.Count(rewrittenFunction,
[]byte("@llvm.experimental.gc.relocate")), 16; got != want {
t.Fatalf("alloca relocates=%d, want %d\n%s", got, want, rewrittenFunction)
[]byte("i64 40, i64 8, i64 8, 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 bytes.Contains(rewrittenFunction, []byte(`"gc-live"`)) ||
bytes.Contains(rewrittenFunction, []byte("@llvm.experimental.gc.relocate")) {
t.Fatalf("alloca leaves became SSA roots\n%s", rewrittenFunction)
}
if got, want := bytes.Count(rewrittenFunction, []byte("store ptr null")), 4; got != want {
t.Fatalf("alloca pointer initializers=%d, want %d\n%s", got, want, rewrittenFunction)
}
runLLVMABICommand(t, rewrittenIR, opt, "-load-pass-plugin="+plugin,
"-passes=verify", "-disable-output", "-")
Expand All @@ -99,6 +108,11 @@ func runLLVMAllocaStatepointTest(t *testing.T, gorootTestDir string) {
got, want, machineFunction)
}
for _, statepoint := range ordinaryStatepoints {
for _, constant := range []string{"1195461697", "1347703373", "40", "29", "1095519299"} {
if !bytes.Contains(statepoint, []byte(constant)) {
t.Fatalf("STATEPOINT lost alloca contract constant %s: %s", constant, statepoint)
}
}
stackObject := regexp.MustCompile(`(%stack\.[^,\s]+)`).FindSubmatch(statepoint)
if len(stackObject) != 2 {
t.Fatalf("STATEPOINT has no alloca frame index: %s", statepoint)
Expand All @@ -109,14 +123,24 @@ func runLLVMAllocaStatepointTest(t *testing.T, gorootTestDir string) {
object, stackObject[1], statepoint)
}
}
for _, offset := range []string{"0", "16", "24", "32"} {
pattern := regexp.QuoteMeta(string(stackObject[1])) + `,\s+` + offset + `(?:\D|$)`
if !regexp.MustCompile(pattern).Match(statepoint) {
t.Fatalf("STATEPOINT does not reuse alloca offset %s: %s", offset, statepoint)
}
pattern := `0,\s+` + regexp.QuoteMeta(string(stackObject[1])) + `,\s+0`
if !regexp.MustCompile(pattern).Match(statepoint) {
t.Fatalf("STATEPOINT does not carry the direct alloca frame base: %s", statepoint)
}
}

goallcAssembly := runLLVMABICommand(t, nil, llc,
"-load-pass-plugin="+plugin, "-verify-machineinstrs",
"-filetype=asm", "-o", "-", goallcIR)
betweenCalls := regexp.MustCompile(`(?s)\bbl\s+p\.mutateLocal\n(.*?)\bbl\s+p\.safepoint`).
FindSubmatch(goallcAssembly)
if len(betweenCalls) != 2 {
t.Fatalf("GoALLC assembly has no adjacent mutateLocal/safepoint calls\n%s", goallcAssembly)
}
if regexp.MustCompile(`(?m)^\s*(?:str|stp)\b`).Match(betweenCalls[1]) {
t.Fatalf("GoALLC restored an alloca field after mutateLocal:\n%s", betweenCalls[1])
}

runLLVMABICommand(t, nil, llc, "-load-pass-plugin="+plugin,
"-filetype=obj", goallcIR, "-o", goallcObject)
symbol := findLLVMABISymbol(t, readLLVMABIObject(t, goallcObject),
Expand Down
35 changes: 35 additions & 0 deletions src/cmd/llvmplugin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,41 @@ if(BUILD_TESTING)
"${CMAKE_CURRENT_SOURCE_DIR}/testdata/alloca-pointer-roots.ll"
)

add_test(
NAME GoALLCStatepoints.MalformedAllocaPointerMapsFail
COMMAND
"${Python3_EXECUTABLE}"
"${CMAKE_CURRENT_SOURCE_DIR}/testdata/check-alloca-ptrmap-malformed.py"
--llc "${GOALLC_LLC_EXECUTABLE}"
--plugin "$<TARGET_FILE:GoALLCStatepoints>"
)

add_test(
NAME GoALLCStatepoints.PointerAllocaSelectFails
COMMAND
"${Python3_EXECUTABLE}"
"${CMAKE_CURRENT_SOURCE_DIR}/testdata/check-statepoint-failure.py"
--llc "${GOALLC_LLC_EXECUTABLE}"
--plugin "$<TARGET_FILE:GoALLCStatepoints>"
--input
"${CMAKE_CURRENT_SOURCE_DIR}/testdata/alloca-pointer-select-unsupported.ll"
--contains
"require pointer-containing alloca PHI/select addresses to resolve to one zero-offset alloca"
)

add_test(
NAME GoALLCStatepoints.NonEntryPointerAllocaFails
COMMAND
"${Python3_EXECUTABLE}"
"${CMAKE_CURRENT_SOURCE_DIR}/testdata/check-statepoint-failure.py"
--llc "${GOALLC_LLC_EXECUTABLE}"
--plugin "$<TARGET_FILE:GoALLCStatepoints>"
--input
"${CMAKE_CURRENT_SOURCE_DIR}/testdata/alloca-pointer-nonentry-unsupported.ll"
--contains
"require a single fixed entry-block pointer-containing alloca"
)

add_test(
NAME GoALLCStatepoints.DynamicPointerAllocaFails
COMMAND
Expand Down
30 changes: 23 additions & 7 deletions src/cmd/llvmplugin/GoALLCStackMapPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
#include "llvm/MC/MCContext.h"
#include "llvm/Support/ErrorHandling.h"

#include <limits>
#include <optional>

using namespace llvm;

namespace {
Expand Down Expand Up @@ -82,17 +85,30 @@ bool GoALLCStackMapPrinter::emitStackMaps(StackMaps &SM, AsmPrinter &AP) {
getNonnegativeConstant(CSI.Locations[2], "deopt count");
if (NumDeopts > CSI.Locations.size() - 3)
report_fatal_error("malformed GoALLC statepoint deopt operands");
size_t FirstGCLocation = 3 + static_cast<size_t>(NumDeopts);
if (NumDeopts > std::numeric_limits<uint32_t>::max())
report_fatal_error("GoALLC statepoint has too many deopt operands");

MCContext::GoObjStackMapEntry Entry{CSI.CSOffsetExpr, CSI.ID,
CSI.IsIndirectCall, Info.StackSize,
PointerSize, {}};
Entry.Locations.reserve(CSI.Locations.size() - FirstGCLocation);
PointerSize,
static_cast<uint32_t>(NumDeopts),
{}};
Entry.Locations.reserve(CSI.Locations.size() - 3);
for (const StackMaps::Location &Location :
ArrayRef(CSI.Locations).drop_front(FirstGCLocation)) {
Entry.Locations.push_back({convertLocationType(Location.Type),
Location.Size, Location.Reg,
Location.Offset});
ArrayRef(CSI.Locations).drop_front(3)) {
auto Type = convertLocationType(Location.Type);
int64_t Offset = Location.Offset;
if (Location.Type == StackMaps::Location::Constant ||
Location.Type == StackMaps::Location::ConstantIndex) {
std::optional<int64_t> Constant = SM.getConstantValue(Location);
if (!Constant)
report_fatal_error(
"GoALLC statepoint contains an invalid constant-pool index");
Type = MCContext::GoObjStackMapLocation::Constant;
Offset = *Constant;
}
Entry.Locations.push_back(
{Type, Location.Size, Location.Reg, Offset});
}
AP.OutContext.addGoObjSymbolStackMapEntry(Function, std::move(Entry));
}
Expand Down
Loading