Skip to content

Commit

Permalink
One iseq_encoded for the whole file (#415)
Browse files Browse the repository at this point in the history
* One iseq_encoded for the whole file

* Fix the double-free

* use a statically-allocated array for iseq_encoded

* fix assertions that didn't happen when running benchmarks

* Fix test errors

* Get rid of some unnecessary functions

* Format

* Update compiler/IREmitter/Payload/vm-payload.c

Co-authored-by: Jake Zimmerman <zimmerman.jake@gmail.com>

* Update exp files on master

* Update exp this branch

Co-authored-by: Nathan Froyd <froydnj@gmail.com>
Co-authored-by: Jake Zimmerman <zimmerman.jake@gmail.com>
  • Loading branch information
3 people committed May 11, 2021
1 parent abbf92e commit 0ff3751
Show file tree
Hide file tree
Showing 51 changed files with 13,613 additions and 15,184 deletions.
1 change: 0 additions & 1 deletion compiler/Core/CompilerState.cc
Expand Up @@ -56,7 +56,6 @@ llvm::FunctionType *CompilerState::getRubyBlockFFIType() {
llvm::FunctionType *CompilerState::getRubyExceptionFFIType() {
llvm::Type *args[] = {
llvm::Type::getInt64PtrTy(lctx)->getPointerTo(), // VALUE **pc
llvm::Type::getInt64PtrTy(lctx), // VALUE *iseq_encoded
llvm::Type::getInt64Ty(lctx), // VALUE captures
};
return llvm::FunctionType::get(llvm::Type::getInt64Ty(lctx), args, false /*not varargs*/);
Expand Down
18 changes: 8 additions & 10 deletions compiler/IREmitter/Exceptions.cc
Expand Up @@ -82,28 +82,26 @@ class ExceptionState {
return irctx.rubyBlocks2Functions[handlersRubyBlockId];
}

// Fetch the pc, iseq_encoded, and closure values that are used when calling an exception function.
tuple<llvm::Value *, llvm::Value *, llvm::Value *> getExceptionArgs() {
// Fetch the pc, and closure values that are used when calling an exception function.
tuple<llvm::Value *, llvm::Value *> getExceptionArgs() {
auto *pc = builder.CreateLoad(irctx.lineNumberPtrsByFunction[rubyBlockId]);
auto *iseq_encoded = builder.CreateLoad(irctx.iseqEncodedPtrsByFunction[rubyBlockId]);
auto *closure = Payload::buildLocalsOffset(cs);
return {pc, iseq_encoded, closure};
return {pc, closure};
}

// Run a function that may raiase exceptions.
tuple<llvm::Value *, llvm::Value *> sorbetTry(llvm::Function *fun, llvm::Value *exceptionContext) {
auto [pc, iseq_encoded, closure] = getExceptionArgs();
auto *result =
builder.CreateCall(cs.getFunction("sorbet_try"),
{fun, pc, iseq_encoded, closure, exceptionContext, exceptionResultPtr}, "result");
auto [pc, closure] = getExceptionArgs();
auto *result = builder.CreateCall(cs.getFunction("sorbet_try"),
{fun, pc, closure, exceptionContext, exceptionResultPtr}, "result");

return {result, exceptionResultPtr};
}

// Run the ensure clause, overwriting the previous return value that was passed in if it's present.
llvm::Value *sorbetEnsure(llvm::Value *previousReturnValue) {
auto [pc, iseq_encoded, closure] = getExceptionArgs();
auto *res = builder.CreateCall(getEnsure(), {pc, iseq_encoded, closure}, "ensureResult");
auto [pc, closure] = getExceptionArgs();
auto *res = builder.CreateCall(getEnsure(), {pc, closure}, "ensureResult");
auto *notUndef = builder.CreateICmpNE(res, Payload::rubyUndef(cs, builder), "ensureReturnValue");
return builder.CreateSelect(notUndef, res, previousReturnValue);
}
Expand Down
11 changes: 3 additions & 8 deletions compiler/IREmitter/IREmitter.cc
Expand Up @@ -58,19 +58,16 @@ void setupStackFrame(CompilerState &cs, const ast::MethodDef &md, const IREmitte
case FunctionType::Rescue:
case FunctionType::Ensure: {
// Switch the current control frame from a C frame to a Ruby-esque one
auto [pc, iseq_encoded] = Payload::setRubyStackFrame(cs, builder, irctx, md, rubyBlockId);
auto pc = Payload::setRubyStackFrame(cs, builder, irctx, md, rubyBlockId);
builder.CreateStore(pc, irctx.lineNumberPtrsByFunction[rubyBlockId]);
builder.CreateStore(iseq_encoded, irctx.iseqEncodedPtrsByFunction[rubyBlockId]);
break;
}

case FunctionType::ExceptionBegin: {
// Exception functions get their pc and iseq_encoded values as arguments
auto func = irctx.rubyBlocks2Functions[rubyBlockId];
auto *pc = func->arg_begin();
auto *iseq_encoded = func->arg_begin() + 1;
builder.CreateStore(pc, irctx.lineNumberPtrsByFunction[rubyBlockId]);
builder.CreateStore(iseq_encoded, irctx.iseqEncodedPtrsByFunction[rubyBlockId]);
break;
}

Expand Down Expand Up @@ -112,9 +109,8 @@ void setupStackFrames(CompilerState &base, const ast::MethodDef &md, const IREmi

setupStackFrame(cs, md, irctx, builder, rubyBlockId);
auto lastLoc = core::Loc::none();
auto startLoc = IREmitterHelpers::getMethodStart(cs, md.symbol);
auto startLoc = md.symbol.data(base)->loc();
Payload::setLineNumber(cs, builder, core::Loc(cs.file, md.loc), startLoc, lastLoc,
irctx.iseqEncodedPtrsByFunction[rubyBlockId],
irctx.lineNumberPtrsByFunction[rubyBlockId]);
}
}
Expand Down Expand Up @@ -500,7 +496,7 @@ llvm::BasicBlock *resolveJumpTarget(cfg::CFG &cfg, const IREmitterContext &irctx
void emitUserBody(CompilerState &base, cfg::CFG &cfg, const IREmitterContext &irctx) {
llvm::IRBuilder<> builder(base);
UnorderedSet<cfg::LocalRef> loadYieldParamsResults; // methods calls on these are ignored
auto startLoc = IREmitterHelpers::getMethodStart(base, cfg.symbol);
auto startLoc = cfg.symbol.data(base)->loc();
auto &arguments = cfg.symbol.data(base)->arguments();
for (auto it = cfg.forwardsTopoSort.rbegin(); it != cfg.forwardsTopoSort.rend(); ++it) {
cfg::BasicBlock *bb = *it;
Expand All @@ -521,7 +517,6 @@ void emitUserBody(CompilerState &base, cfg::CFG &cfg, const IREmitterContext &ir
auto loc = core::Loc(cs.file, bind.loc);

lastLoc = Payload::setLineNumber(cs, builder, loc, startLoc, lastLoc,
irctx.iseqEncodedPtrsByFunction[bb->rubyBlockId],
irctx.lineNumberPtrsByFunction[bb->rubyBlockId]);

IREmitterHelpers::emitDebugLoc(cs, builder, irctx, bb->rubyBlockId, loc);
Expand Down
3 changes: 0 additions & 3 deletions compiler/IREmitter/IREmitterContext.h
Expand Up @@ -197,9 +197,6 @@ struct IREmitterContext {
// TODO(jez) document lineNumberPtrsByFunction
std::vector<llvm::AllocaInst *> lineNumberPtrsByFunction;

// TODO(jez) document iseqEncodedPtrsByFunction
std::vector<llvm::AllocaInst *> iseqEncodedPtrsByFunction;

// TODO(jez) usesBlockArgs
bool usesBlockArgs;

Expand Down
17 changes: 1 addition & 16 deletions compiler/IREmitter/IREmitterHelpers.cc
Expand Up @@ -359,8 +359,7 @@ void getRubyBlocks2FunctionsMapping(CompilerState &cs, cfg::CFG &cfg, llvm::Func

// argument names
fp->arg_begin()->setName("pc");
(fp->arg_begin() + 1)->setName("iseq_encoded");
(fp->arg_begin() + 2)->setName("localsOffset");
(fp->arg_begin() + 1)->setName("localsOffset");

funcs[i] = fp;
break;
Expand Down Expand Up @@ -552,11 +551,9 @@ IREmitterContext IREmitterHelpers::getSorbetBlocks2LLVMBlockMapping(CompilerStat
vector<llvm::BasicBlock *> userEntryBlockByFunction(rubyBlock2Function.size());
vector<llvm::AllocaInst *> sendArgArrayByBlock;
vector<llvm::AllocaInst *> lineNumberPtrsByFunction;
vector<llvm::AllocaInst *> iseqEncodedPtrsByFunction;

int i = 0;
auto lineNumberPtrType = llvm::PointerType::getUnqual(llvm::Type::getInt64PtrTy(cs));
auto iseqEncodedPtrType = llvm::Type::getInt64PtrTy(cs);
for (auto &fun : rubyBlock2Function) {
auto inits = functionInitializersByFunction.emplace_back(llvm::BasicBlock::Create(
cs, "functionEntryInitializers",
Expand All @@ -567,8 +564,6 @@ IREmitterContext IREmitterHelpers::getSorbetBlocks2LLVMBlockMapping(CompilerStat
sendArgArrayByBlock.emplace_back(sendArgArray);
auto lineNumberPtr = builder.CreateAlloca(lineNumberPtrType, nullptr, "lineCountStore");
lineNumberPtrsByFunction.emplace_back(lineNumberPtr);
auto iseqEncodedPtr = builder.CreateAlloca(iseqEncodedPtrType, nullptr, "iseqEncodedStore");
iseqEncodedPtrsByFunction.emplace_back(iseqEncodedPtr);
argumentSetupBlocksByFunction.emplace_back(llvm::BasicBlock::Create(cs, "argumentSetup", fun));
i++;
}
Expand Down Expand Up @@ -718,7 +713,6 @@ IREmitterContext IREmitterHelpers::getSorbetBlocks2LLVMBlockMapping(CompilerStat
move(blockParents),
move(blockLevels),
move(lineNumberPtrsByFunction),
move(iseqEncodedPtrsByFunction),
usesBlockArgs,
move(exceptionHandlingBlockHeaders),
move(deadBlocks),
Expand Down Expand Up @@ -791,15 +785,6 @@ bool IREmitterHelpers::isFileOrClassStaticInit(const core::GlobalState &gs, core
return isFileStaticInit(gs, sym) || isClassStaticInit(gs, sym);
}

core::Loc IREmitterHelpers::getMethodLineBounds(const core::GlobalState &gs, core::SymbolRef sym, core::FileRef file,
core::LocOffsets offsets) {
if (IREmitterHelpers::isFileStaticInit(gs, sym)) {
return core::Loc(file, core::LocOffsets{0, offsets.endLoc});
} else {
return core::Loc(file, offsets);
}
}

namespace {
llvm::GlobalValue::LinkageTypes getFunctionLinkageType(CompilerState &cs, core::SymbolRef sym) {
if (IREmitterHelpers::isFileOrClassStaticInit(cs, sym)) {
Expand Down
10 changes: 0 additions & 10 deletions compiler/IREmitter/IREmitterHelpers.h
Expand Up @@ -62,16 +62,6 @@ class IREmitterHelpers {
static bool isFileStaticInit(const core::GlobalState &gs, core::SymbolRef sym);
static bool isFileOrClassStaticInit(const core::GlobalState &gs, core::SymbolRef sym);

// Returns a core::Loc whose start and end positions containt the bounds of the method sym.
static core::Loc getMethodLineBounds(const core::GlobalState &gs, core::SymbolRef sym, core::FileRef file,
core::LocOffsets offsets);

// Returns a core::Loc whose begin pos contains the start line of the method.
static core::Loc getMethodStart(const core::GlobalState &gs, core::SymbolRef sym) {
auto loc = sym.data(gs)->loc();
return getMethodLineBounds(gs, sym, loc.file(), loc.offsets());
}

static std::string getFunctionName(CompilerState &cs, core::SymbolRef sym);
static llvm::Function *lookupFunction(CompilerState &cs, core::SymbolRef sym);
static llvm::Function *getOrCreateFunctionWeak(CompilerState &cs, core::SymbolRef sym);
Expand Down
96 changes: 71 additions & 25 deletions compiler/IREmitter/Payload.cc
Expand Up @@ -583,30 +583,19 @@ llvm::Function *allocateRubyStackFramesImpl(CompilerState &cs, const IREmitterCo
// We are building a new function. We should redefine where do function initializers go
auto cs1 = cs.withFunctionEntry(bei);

auto loc = IREmitterHelpers::getMethodLineBounds(cs, md.symbol, cs.file, md.loc);
auto file = cs.file;
auto *iseqType = getIseqType(cs1, builder, irctx, rubyBlockId);
auto [funcName, parent] = getIseqInfo(cs1, builder, irctx, md, rubyBlockId);
auto funcNameId = Payload::idIntern(cs1, builder, funcName);
auto funcNameValue = Payload::cPtrToRubyString(cs1, builder, funcName, true);
auto filename = loc.file().data(cs).path();
auto filename = file.data(cs).path();
auto filenameValue = Payload::cPtrToRubyString(cs1, builder, filename, true);
// The method might have been synthesized by Sorbet (e.g. in the case of packages).
// Give such methods line numbers of 0.
unsigned startLine, endLine;
if (loc.exists()) {
startLine = loc.position(cs).first.line;
endLine = loc.position(cs).second.line;
} else {
startLine = 0;
endLine = 0;
}
auto [locals, numLocals] = getLocals(cs1, builder, irctx, md, rubyBlockId);
auto sendMax = llvm::ConstantInt::get(cs, llvm::APInt(32, irctx.maxSendArgCount, true));
auto *fileLineNumberInfo = Payload::getFileLineNumberInfo(cs, builder, file);
auto *fn = cs.getFunction("sorbet_allocateRubyStackFrame");
auto ret =
builder.CreateCall(fn, {funcNameValue, funcNameId, filenameValue, realpath, parent, iseqType,
llvm::ConstantInt::get(cs, llvm::APInt(32, startLine)),
llvm::ConstantInt::get(cs, llvm::APInt(32, endLine)), locals, numLocals, sendMax});
auto ret = builder.CreateCall(fn, {funcNameValue, funcNameId, filenameValue, realpath, parent, iseqType,
fileLineNumberInfo, locals, numLocals, sendMax});
auto zero = llvm::ConstantInt::get(cs, llvm::APInt(64, 0));
llvm::Constant *indices[] = {zero};
builder.CreateStore(ret, llvm::ConstantExpr::getInBoundsGetElementPtr(store->getValueType(), store, indices));
Expand Down Expand Up @@ -689,9 +678,8 @@ llvm::Value *Payload::rubyStackFrameVar(CompilerState &cs, llvm::IRBuilderBase &
return ::sorbet::compiler::rubyStackFrameVar(cs, build, irctx, methodSym, 0);
}

std::pair<llvm::Value *, llvm::Value *> Payload::setRubyStackFrame(CompilerState &cs, llvm::IRBuilderBase &build,
const IREmitterContext &irctx,
const ast::MethodDef &md, int rubyBlockId) {
llvm::Value *Payload::setRubyStackFrame(CompilerState &cs, llvm::IRBuilderBase &build, const IREmitterContext &irctx,
const ast::MethodDef &md, int rubyBlockId) {
auto &builder = builderCast(build);
auto stackFrame = allocateRubyStackFrames(cs, builder, irctx, md, rubyBlockId);
auto *iseqType = getIseqType(cs, builder, irctx, rubyBlockId);
Expand All @@ -700,8 +688,7 @@ std::pair<llvm::Value *, llvm::Value *> Payload::setRubyStackFrame(CompilerState
irctx.rubyBlockType[rubyBlockId] == FunctionType::StaticInitModule)));
auto cfp = builder.CreateCall(cs.getFunction("sorbet_setRubyStackFrame"), {isStaticInit, iseqType, stackFrame});
auto pc = builder.CreateCall(cs.getFunction("sorbet_getPc"), {cfp});
auto iseq_encoded = builder.CreateCall(cs.getFunction("sorbet_getIseqEncoded"), {cfp});
return {pc, iseq_encoded};
return pc;
}

// Ensure that the retry singleton is present during module initialization, and store it in a module-local global.
Expand Down Expand Up @@ -751,8 +738,62 @@ llvm::Value *Payload::voidSingleton(CompilerState &cs, llvm::IRBuilderBase &buil
return builderCast(build).CreateLoad(global, rawName);
}

// Lazily initialize a global that contains enough noops to represent all the lines in the file as an iseq_encoded
// array.
llvm::Value *Payload::getFileLineNumberInfo(CompilerState &cs, llvm::IRBuilderBase &build, core::FileRef file) {
auto *iseqEncodedInitFn = cs.module->getFunction("sorbet_initLineNumberInfo");
auto *infoPointerTy = iseqEncodedInitFn->getFunctionType()->params()[0];
ENFORCE(infoPointerTy != nullptr);

auto *globalTy = llvm::cast<llvm::PointerType>(infoPointerTy)->getElementType();

auto *iseqEncoded = getIseqEncodedPointer(cs, build, file);
const string rawName = "fileLineNumberInfo";
auto *global = cs.module->getOrInsertGlobal(
rawName, globalTy, [&cs, &rawName, &iseqEncoded, file, globalTy, iseqEncodedInitFn]() {
auto globalInitBuilder = llvm::IRBuilder<>(cs);

bool isConstant = false;
auto *zero = llvm::ConstantAggregateZero::get(globalTy);
auto *fileLineNumberInfo = new llvm::GlobalVariable(*cs.module, globalTy, isConstant,
llvm::GlobalVariable::InternalLinkage, zero, rawName);

globalInitBuilder.SetInsertPoint(cs.globalConstructorsEntry);

auto *numLines = llvm::ConstantInt::get(cs, llvm::APInt(32, file.data(cs).lineCount(), true));
auto *intzero = llvm::ConstantInt::get(cs, llvm::APInt(32, 0));
llvm::Value *indices[] = {intzero, intzero};
globalInitBuilder.CreateCall(
iseqEncodedInitFn, {fileLineNumberInfo, globalInitBuilder.CreateGEP(iseqEncoded, indices), numLines});

return fileLineNumberInfo;
});

return global;
}

llvm::Value *Payload::getIseqEncodedPointer(CompilerState &cs, llvm::IRBuilderBase &builder, core::FileRef file) {
auto *int64Ty = llvm::Type::getInt64Ty(cs);
uint32_t lineCount = file.data(cs).lineCount();
auto *globalTy = llvm::ArrayType::get(int64Ty, lineCount);

const string rawName = "iseqEncodedArray";
auto *global = cs.module->getOrInsertGlobal(rawName, globalTy, [&cs, &rawName, globalTy]() {
auto globalInitBuilder = llvm::IRBuilder<>(cs);

bool isConstant = false;
auto *zero = llvm::ConstantAggregateZero::get(globalTy);
auto *iseqEncodedArray = new llvm::GlobalVariable(*cs.module, globalTy, isConstant,
llvm::GlobalVariable::InternalLinkage, zero, rawName);

return iseqEncodedArray;
});

return global;
}

core::Loc Payload::setLineNumber(CompilerState &cs, llvm::IRBuilderBase &build, core::Loc loc, core::Loc methodStart,
core::Loc lastLoc, llvm::AllocaInst *iseqEncodedPtr, llvm::AllocaInst *lineNumberPtr) {
core::Loc lastLoc, llvm::AllocaInst *lineNumberPtr) {
if (!loc.exists()) {
return lastLoc;
}
Expand All @@ -764,10 +805,15 @@ core::Loc Payload::setLineNumber(CompilerState &cs, llvm::IRBuilderBase &build,
if (!methodStart.exists()) {
return lastLoc;
}
auto offset = lineno - methodStart.position(cs).first.line;

// turn the line number into an offset into the iseq_encoded global array
auto *offset = llvm::ConstantInt::get(cs, llvm::APInt(32, lineno - 1));

auto *encoded = Payload::getIseqEncodedPointer(cs, builder, loc.file());
auto *intzero = llvm::ConstantInt::get(cs, llvm::APInt(32, 0));
llvm::Value *indices[] = {intzero, intzero};
builder.CreateCall(cs.getFunction("sorbet_setLineNumber"),
{llvm::ConstantInt::get(cs, llvm::APInt(32, offset)), builder.CreateLoad(iseqEncodedPtr),
builder.CreateLoad(lineNumberPtr)});
{offset, builder.CreateGEP(encoded, indices), builder.CreateLoad(lineNumberPtr)});
return loc;
}

Expand Down
11 changes: 6 additions & 5 deletions compiler/IREmitter/Payload.h
Expand Up @@ -62,9 +62,8 @@ class Payload {
static llvm::Value *typeTestForBlock(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *val,
const core::TypePtr &type);
static llvm::Value *boolToRuby(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *u1);
static std::pair<llvm::Value *, llvm::Value *> setRubyStackFrame(CompilerState &cs, llvm::IRBuilderBase &builder,
const IREmitterContext &irctx,
const ast::MethodDef &md, int rubyBlockId);
static llvm::Value *setRubyStackFrame(CompilerState &cs, llvm::IRBuilderBase &builder,
const IREmitterContext &irctx, const ast::MethodDef &md, int rubyBlockId);

static llvm::Value *readKWRestArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *maybeHash);
static llvm::Value *assertNoExtraKWArg(CompilerState &cs, llvm::IRBuilderBase &builder, llvm::Value *maybeHash);
Expand All @@ -73,8 +72,7 @@ class Payload {
static llvm::Value *readRestArgs(CompilerState &cs, llvm::IRBuilderBase &builder, int maxPositionalArgCount,
llvm::Value *argCountRaw, llvm::Value *argArrayRaw);
static core::Loc setLineNumber(CompilerState &cs, llvm::IRBuilderBase &builder, core::Loc loc,
core::Loc methodStart, core::Loc lastLoc, llvm::AllocaInst *iseqEncodedPtr,
llvm::AllocaInst *lineNumberPtr);
core::Loc methodStart, core::Loc lastLoc, llvm::AllocaInst *lineNumberPtr);
static llvm::Value *varGet(CompilerState &cs, cfg::LocalRef local, llvm::IRBuilderBase &builder,
const IREmitterContext &irctx, int rubyBlockId);
static void varSet(CompilerState &cs, cfg::LocalRef local, llvm::Value *var, llvm::IRBuilderBase &builder,
Expand All @@ -100,6 +98,9 @@ class Payload {
static llvm::Value *rubyStackFrameVar(CompilerState &cs, llvm::IRBuilderBase &builder,
const IREmitterContext &irctx, core::SymbolRef methodSym);

static llvm::Value *getFileLineNumberInfo(CompilerState &gs, llvm::IRBuilderBase &builder, core::FileRef file);
static llvm::Value *getIseqEncodedPointer(CompilerState &gs, llvm::IRBuilderBase &builder, core::FileRef file);

static const VMFlag VM_CALL_ARGS_SIMPLE;
static const VMFlag VM_CALL_ARGS_SPLAT;
static const VMFlag VM_CALL_KWARG;
Expand Down

0 comments on commit 0ff3751

Please sign in to comment.