blob: fe219743e5aaf54930d6addeb0263d0652683225 [file] [log] [blame]
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include <utility>
#include "dart.h"
#include "llvm/ADT/StringSwitch.h"
using namespace llvm;
Value* DartThreadObject::GetOffset(Type* type, intptr_t offset) const {
auto& ctx = bb_builder_.Context();
auto& builder = bb_builder_.Builder();
auto int64ty = IntegerType::getInt64Ty(ctx);
// TODO: This is only correct for x86_64. On x86 we need to use 257,
// and only arm targets an entirely different mechanism will be
// required since there isn't an unused register. On Arm we can
// probably still use the thread register as long as we're careful
// to set it back on thread boundaries.
// On x86_64 fs (257) is used for TLS but gs (256) is unused
// On x86 gs (256) is used for TLS but fs (257) is unused
// we use the unused segment so as to not conflict with TLS which
// allows linking against native code that uses TLS without needing
// to handle any kind of context switching.
constexpr unsigned kDartThreadPointerAddressSpace = 256;
auto* ptr_tls = PointerType::get(type, kDartThreadPointerAddressSpace);
auto* offset_value = ConstantInt::get(int64ty, offset);
auto* tls_value = builder.CreateIntToPtr(offset_value, ptr_tls);
return builder.CreateLoad(tls_value);
}
Value* DartThreadObject::StackLimit() const {
auto& ctx = bb_builder_.Context();
return GetOffset(IntegerType::getInt64Ty(ctx), kThreadStackLimitOffset);
}
Value* BasicBlockBuilder::GetValue(const DartValue* v) {
auto iter = values_.find(v);
if (iter == values_.end()) {
auto* out = v->Make(*this);
values_[v] = out;
return out;
}
return iter->second;
}
DartInstruction::~DartInstruction() {}
static Error CreateError(const Twine& err) {
return make_error<StringError>(err, inconvertibleErrorCode());
}
Value* DartConstant::Make(BasicBlockBuilder& bb_builder) const {
auto& ctx = bb_builder.Context();
switch (type) {
case DartConstant::Type::String:
auto constant = ConstantDataArray::getString(ctx, str);
auto gv =
new GlobalVariable(bb_builder.Module(), constant->getType(), true,
GlobalVariable::ExternalLinkage, constant);
return ConstantExpr::getBitCast(gv, GetType(bb_builder));
}
}
Type* DartConstant::GetType(BasicBlockBuilder& bb_builder) const {
// TODO: Right now this returns a c-string type but that's not correct.
// We should move this to a generic object type (e.g. a tagged pointer).
auto& ctx = bb_builder.Context();
auto int8ty = IntegerType::getInt8Ty(ctx);
return PointerType::get(int8ty, 0);
}
void InstCheckStackOverflow::Build(BasicBlockBuilder& bb_builder) const {
auto& builder = bb_builder.Builder();
auto& ctx = bb_builder.Context();
auto& module = bb_builder.Module();
auto& thread_object = bb_builder.ThreadObject();
// TODO: This is only correct on 64-bit architectures.
auto int64ty = IntegerType::getInt64Ty(ctx);
// Get the stack pointer.
auto spi_type = Intrinsic::getType(ctx, Intrinsic::stacksave);
auto spi_func = Intrinsic::getDeclaration(&module, Intrinsic::stacksave);
auto sp_raw = builder.CreateCall(spi_type, spi_func);
auto sp = builder.CreatePtrToInt(sp_raw, int64ty);
// Get the stack limit from the thread pointer.
auto stack_limit = thread_object.StackLimit();
// Now compare the stack pointer and limit.
auto error_bb = bb_builder.AddBasicBlock();
auto cont_bb = bb_builder.AddBasicBlock();
auto cmp = builder.CreateICmpULT(sp, stack_limit);
builder.CreateCondBr(cmp, error_bb, cont_bb);
// Now build the error path.
// TODO: Don't just trap here. For now we just trap rather than
// handling the proper exceptional control flow here.
builder.SetInsertPoint(error_bb);
auto trap_type = Intrinsic::getType(ctx, Intrinsic::trap);
auto trap_func = Intrinsic::getDeclaration(&module, Intrinsic::trap);
builder.CreateCall(trap_type, trap_func);
builder.CreateBr(cont_bb);
// Now pretend as if this new block is just the end of the block we
// started with.
builder.SetInsertPoint(cont_bb);
}
Expected<std::unique_ptr<DartInstruction>> InstCheckStackOverflow::Construct(
dart::SExpList* inst,
DartBasicBlockBuilder& bb_builder) {
// inst = (CheckStackoverflow)
return llvm::make_unique<InstCheckStackOverflow>();
}
void InstPushArgument::Build(BasicBlockBuilder& bb_builder) const {
bb_builder.PushArgument(bb_builder.GetValue(arg_));
}
Expected<std::unique_ptr<DartInstruction>> InstPushArgument::Construct(
dart::SExpList* inst,
DartBasicBlockBuilder& bb_builder) {
// inst = (PushArgument <arg>)
if (inst->Length() != 2) {
return CreateError("PushArgument should have exactly 1 argument");
}
dart::SExpSymbol* arg = inst->At(1)->AsSymbol();
if (arg == nullptr) {
return CreateError("Expected PushArgument's argument to be a symbol");
}
const DartValue* dvalue = bb_builder.GetDef(arg->value());
if (dvalue == nullptr) {
return CreateError(Twine(arg->value()) + " is not a valid symbol");
}
return llvm::make_unique<InstPushArgument>(dvalue);
}
void InstStaticCall::Build(BasicBlockBuilder& bb_builder) const {
// inst = (StaticCall <function-symbol> <arg> ...)
SmallVector<Value*, 8> args;
size_t arg_count = args_len_;
while (arg_count > 0) {
arg_count--;
args.push_back(bb_builder.PopArgument());
}
auto& builder = bb_builder.Builder();
// Hard code the function type for now.
auto& ctx = bb_builder.Context();
auto int8ty = IntegerType::getInt8Ty(ctx);
auto i8ptr = PointerType::get(int8ty, 0);
SmallVector<Type*, 8> arg_types;
arg_types.push_back(i8ptr);
auto print_type = FunctionType::get(Type::getVoidTy(ctx), arg_types, false);
// Get the function value we need.
auto func = bb_builder.GetValue(function_);
builder.CreateCall(print_type, func, args);
}
Expected<std::unique_ptr<DartInstruction>> InstStaticCall::Construct(
dart::SExpList* inst,
DartBasicBlockBuilder& bb_builder) {
// inst = (StaticCall <function_name> { args_len <N> })
if (inst->Length() != 2) {
return CreateError("StaticCall should have exactly 1 argument");
}
dart::SExpSymbol* func = inst->At(1)->AsSymbol();
if (!func) {
return CreateError("Expected StaticCall's argument to be a symbol");
}
const DartValue* dvalue = bb_builder.GetDef(func->value());
dart::SExpression* args_len_expr = inst->ExtraLookupValue("args_len");
// If args_len_expr isn't found args_len is assumed to be zero.
size_t args_len = 0;
if (args_len_expr) {
dart::SExpInteger* args_len_expr_int = args_len_expr->AsInteger();
if (args_len_expr_int) args_len = args_len_expr_int->value();
}
return llvm::make_unique<InstStaticCall>(dvalue, 1);
}
void InstReturn::Build(BasicBlockBuilder& bb_builder) const {
auto& builder = bb_builder.Builder();
builder.CreateRetVoid();
}
Expected<std::unique_ptr<DartInstruction>> InstReturn::Construct(
dart::SExpList* inst,
DartBasicBlockBuilder& bb_builder) {
// inst = (Return)
return llvm::make_unique<InstReturn>();
}
static Expected<StringMap<DartConstant>> MakeConstants(dart::Zone* zone,
dart::SExpList* sexpr) {
StringMap<DartConstant> out;
for (intptr_t i = 1; i < sexpr->Length(); ++i) {
DartConstant constant;
dart::SExpList* def = sexpr->At(i)->AsList();
if (!def) {
return CreateError(Twine("Stray token in constants at location ") +
Twine(i) + sexpr->At(i)->ToCString(zone));
}
if (def->Length() != 3) {
return CreateError("Constant definitions must have exactly 3 lines");
}
dart::SExpSymbol* def_symbol = def->At(0)->AsSymbol();
if (def_symbol->value() != StringRef("def")) {
return CreateError(
"first element in a constant definition expected to be `def`");
}
dart::SExpSymbol* def_name = def->At(1)->AsSymbol();
if (!def_name) {
return CreateError("element after `def` in constant expected to be name");
}
dart::SExpression* def_value = def->At(2);
if (def_value->IsString()) {
constant.str = def_value->AsString()->value();
constant.type = DartConstant::Type::String;
} else {
return CreateError("We can't yet handle that element type");
}
out[def_name->value()] = constant;
}
return out;
}
#define FOREACH_INSTRUCTION(M) \
M(CheckStackOverflow) \
M(PushArgument) \
M(StaticCall) \
M(Return)
static Expected<std::unique_ptr<DartInstruction>> MakeInstruction(
dart::SExpList* sexpr,
DartBasicBlockBuilder& bb_builder) {
if (sexpr->Length() < 1)
return CreateError("An empty list can't be an instruction");
dart::SExpSymbol* inst_sym = sexpr->At(0)->AsSymbol();
if (!inst_sym)
return CreateError(
"Expected first element of list in instruction to be a symbol");
using CtorFunc = std::function<Expected<std::unique_ptr<DartInstruction>>(
dart::SExpList*, DartBasicBlockBuilder&)>;
CtorFunc ctor = StringSwitch<CtorFunc>(inst_sym->value())
#define HANDLE_INSTRUCTION_CASE(INST) .Case(#INST, Inst##INST::Construct)
FOREACH_INSTRUCTION(HANDLE_INSTRUCTION_CASE);
#undef HANDLE_INSTRUCTION_CASE
return ctor(sexpr, bb_builder);
}
static Expected<DartBlock> MakeBlock(dart::SExpList* sexpr,
DartFunction& function,
const StringMap<const DartValue*>& env) {
// Construct the basic block builder
DartBasicBlockBuilder bb_builder;
for (const auto& c : function.constants)
bb_builder.AddDef(c.getKey(), &c.getValue());
for (const auto& c : env)
bb_builder.AddDef(c.getKey(), c.getValue());
DartBlock out;
// Make sure we have a basic block and get the name
if (sexpr->Length() <= 2)
return CreateError("too few elements in basic block");
dart::SExpSymbol* block_name = sexpr->At(1)->AsSymbol();
if (!block_name)
return CreateError("expected block name after `block` symbol");
out.name = block_name->value();
// Now construct each instruction and add it to the basic block
for (intptr_t i = 2; i < sexpr->Length(); ++i) {
dart::SExpList* inst = sexpr->At(i)->AsList();
if (!inst)
return CreateError("stray token at element " + Twine(i) +
" in basic block " + out.name);
auto inst_or = MakeInstruction(inst, bb_builder);
if (!inst_or) return inst_or.takeError();
out.instructions.emplace_back(std::move(*inst_or));
}
return out;
}
Expected<DartFunction> MakeFunction(dart::Zone* zone,
dart::SExpression* sexpr,
const StringMap<const DartValue*>& env) {
// Basic checking that this s-expression looks like a function
dart::SExpList* flist = sexpr->AsList();
if (!flist) return CreateError("S-Expression was not a function list");
if (flist->Length() < 2)
return CreateError("S-Expression list was too short to be a function");
dart::SExpSymbol* function_symbol = flist->At(0)->AsSymbol();
if (function_symbol == nullptr ||
function_symbol->value() != StringRef("function"))
return CreateError(
"S-Expression cannot be a function as it does not start with "
"`function`");
dart::SExpSymbol* function_name = flist->At(1)->AsSymbol();
if (function_name == nullptr)
return CreateError("Expected symbol name after `function` symbol");
// Now we fill in all the details
DartFunction function;
function.name = function_name->value();
Optional<StringRef> normal_entry;
for (intptr_t i = 2; i < flist->Length(); ++i) {
dart::SExpList* chunk = flist->At(i)->AsList();
// Everything is a list so far so error out on other options
if (!chunk) {
return CreateError(Twine("Stray token in function at location ") +
Twine(i) + ": " + flist->At(i)->ToCString(zone));
}
dart::SExpSymbol* chunk_symbol = chunk->At(0)->AsSymbol();
if (!chunk_symbol)
return CreateError(Twine("Expected element ") + Twine(i) +
" of function to start with a symbol");
StringRef chunk_tag = chunk_symbol->value();
if (chunk_tag == "constants") {
auto constants = MakeConstants(zone, chunk);
if (!constants) return constants.takeError();
function.constants = std::move(*constants);
}
if (chunk_tag == "block") {
auto block = MakeBlock(chunk, function, env);
if (!block) return block.takeError();
StringRef name = block->name;
function.blocks[name] = std::move(*block);
}
if (chunk_tag == "normal-entry") {
if (chunk->Length() != 2)
return CreateError("Expected 1 argument to normal-entry");
dart::SExpSymbol* block_name = chunk->At(1)->AsSymbol();
if (!block_name)
return CreateError("expected block name after normal-entry symbol");
normal_entry = block_name->value();
}
}
if (normal_entry) {
auto iter = function.blocks.find(*normal_entry);
if (iter != function.blocks.end())
function.normal_entry = &iter->getValue();
} else {
function.normal_entry = nullptr;
}
return function;
}