| // 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; | |
| } |