// 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 #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(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> InstCheckStackOverflow::Construct( dart::SExpList* inst, DartBasicBlockBuilder& bb_builder) { // inst = (CheckStackoverflow) return llvm::make_unique(); } void InstPushArgument::Build(BasicBlockBuilder& bb_builder) const { bb_builder.PushArgument(bb_builder.GetValue(arg_)); } Expected> InstPushArgument::Construct( dart::SExpList* inst, DartBasicBlockBuilder& bb_builder) { // inst = (PushArgument ) 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(dvalue); } void InstStaticCall::Build(BasicBlockBuilder& bb_builder) const { // inst = (StaticCall ...) SmallVector 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 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> InstStaticCall::Construct( dart::SExpList* inst, DartBasicBlockBuilder& bb_builder) { // inst = (StaticCall { args_len }) 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(dvalue, 1); } void InstReturn::Build(BasicBlockBuilder& bb_builder) const { auto& builder = bb_builder.Builder(); builder.CreateRetVoid(); } Expected> InstReturn::Construct( dart::SExpList* inst, DartBasicBlockBuilder& bb_builder) { // inst = (Return) return llvm::make_unique(); } static Expected> MakeConstants(dart::Zone* zone, dart::SExpList* sexpr) { StringMap 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> 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>( dart::SExpList*, DartBasicBlockBuilder&)>; CtorFunc ctor = StringSwitch(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 MakeBlock(dart::SExpList* sexpr, DartFunction& function, const StringMap& 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 MakeFunction(dart::Zone* zone, dart::SExpression* sexpr, const StringMap& 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 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; }