Exemplo n.º 1
0
TCResult ast::IfStmt::typecheck(sst::TypecheckState* fs, fir::Type* infer)
{
	fs->pushLoc(this);
	defer(fs->popLoc());

	using Case = sst::IfStmt::Case;
	auto ret = util::pool<sst::IfStmt>(this->loc);

	fs->pushAnonymousTree();
	defer(fs->popTree());

	for(auto c : this->cases)
	{
		auto inits = util::map(c.inits, [fs](Stmt* s) -> auto { return s->typecheck(fs).stmt(); });
		auto cs = Case(c.cond->typecheck(fs).expr(), dcast(sst::Block, c.body->typecheck(fs).stmt()), inits);

		if(!cs.cond->type->isBoolType() && !cs.cond->type->isPointerType())
			error(cs.cond, "non-boolean expression with type '%s' cannot be used as a conditional", cs.cond->type);

		ret->cases.push_back(cs);

		iceAssert(ret->cases.back().body);
	}

	if(this->elseCase)
	{
		ret->elseCase = dcast(sst::Block, this->elseCase->typecheck(fs).stmt());
		iceAssert(ret->elseCase);
	}

	return TCResult(ret);
}
Exemplo n.º 2
0
static void checkTuple(cgn::CodegenState* cs, const DecompMapping& bind, CGResult rhs)
{
	iceAssert(!bind.array);

	auto rt = rhs.value->getType();
	iceAssert(rt->isTupleType());

	auto tty = rt->toTupleType();
	iceAssert(bind.inner.size() == tty->getElementCount());

	for(size_t i = 0; i < tty->getElementCount(); i++)
	{
		CGResult v;

		if(rhs->islorclvalue())
		{
			auto gep = cs->irb.StructGEP(rhs.value, i);
			v = CGResult(gep);
		}
		else
		{
			v = CGResult(cs->irb.ExtractValue(rhs.value, { i }));
		}

		cs->generateDecompositionBindings(bind.inner[i], v, true);
	}
}
Exemplo n.º 3
0
TCResult ast::EnumDefn::typecheck(sst::TypecheckState* fs, fir::Type* infer, const TypeParamMap_t& gmaps)
{
	fs->pushLoc(this);
	defer(fs->popLoc());

	auto tcr = this->generateDeclaration(fs, infer, gmaps);
	if(tcr.isParametric())  return tcr;
	else if(tcr.isError())  error(this, "failed to generate declaration for enum '%s'", this->name);

	auto defn = dcast(sst::EnumDefn, tcr.defn());
	iceAssert(defn);

	auto oldscope = fs->getCurrentScope();
	fs->teleportToScope(defn->id.scope);
	fs->pushTree(defn->id.name);

	if(this->memberType)	defn->memberType = fs->convertParserTypeToFIR(this->memberType);
	else					defn->memberType = fir::Type::getInt64();

	auto ety = fir::EnumType::get(defn->id, defn->memberType);

	size_t index = 0;
	for(auto cs : this->cases)
	{
		sst::Expr* val = 0;
		if(cs.value)
		{
			iceAssert(defn->memberType);
			val = cs.value->typecheck(fs, defn->memberType).expr();

			if(val->type != defn->memberType)
				error(cs.value, "mismatched type in enum case value; expected type '%s', but found type '%s'", defn->memberType, val->type);
		}

		auto ecd = util::pool<sst::EnumCaseDefn>(cs.loc);
		ecd->id = Identifier(cs.name, IdKind::Name);
		ecd->id.scope = fs->getCurrentScope();
		ecd->type = ety;
		ecd->parentEnum = defn;
		ecd->val = val;
		ecd->index = index++;

		defn->cases[cs.name] = ecd;
		fs->stree->addDefinition(cs.name, ecd);
	}

	defn->type = ety;

	fs->popTree();
	fs->teleportToScope(oldscope);

	return TCResult(defn);
}
Exemplo n.º 4
0
void OverloadError::post()
{
	// first, post the original error.
	this->top->wordsBeforeContext = "(call site)";
	this->top->post();

	// sort the candidates by line number
	// (idk maybe it's a windows thing but it feels like the order changes every so often)
	auto cds = std::vector<std::pair<Locatable*, ErrorMsg*>>(this->cands.begin(), this->cands.end());
	std::sort(cds.begin(), cds.end(), [](auto a, auto b) -> bool { return a.first->loc.line < b.first->loc.line; });

	// go through each candidate.
	int cand_counter = 1;
	for(auto [ loc, emg ] : cds)
	{
		if(emg->kind != ErrKind::Span)
		{
			emg->post();
		}
		else
		{
			auto spe = dcast(SpanError, emg);
			iceAssert(spe);

			spe->top = SimpleError::make(MsgType::Note, loc->loc, "candidate %d was defined here:", cand_counter++);
			spe->highlightActual = false;
			spe->post();
		}
	}

	for(auto sub : this->subs)
		sub->post();
}
Exemplo n.º 5
0
	void CodegenInstance::addGlobalConstructor(std::string name, llvm::Function* constructor)
	{
		llvm::GlobalVariable* gv = this->module->getGlobalVariable(name);
		iceAssert(gv);

		this->globalConstructors[gv] = constructor;
	}
Exemplo n.º 6
0
	void IRBlock::eraseFromParentFunction()
	{
		iceAssert(this->parentFunction && "no function");
		std::vector<IRBlock*>& blist = this->parentFunction->getBlockList();

		for(auto it = blist.begin(); it != blist.end(); it++)
		{
			if(*it == this)
			{
				blist.erase(it);
				return;
			}
		}

		iceAssert(0 && "not in function");
	}
Exemplo n.º 7
0
	sst::DefinitionTree* typecheckFiles(CollectorState* state)
	{
		// typecheck
		for(const auto& file : state->allFiles)
		{
			// note that we're guaranteed (because that's the whole point)
			// that any module we encounter here will have had all of its dependencies checked already

			std::vector<std::pair<ImportThing, sst::StateTree*>> imports;
			for(auto d : state->graph->getDependenciesOf(file))
			{
				auto imported = d->to;

				auto stree = state->dtrees[imported->name]->base;
				iceAssert(stree);

				ImportThing ithing { imported->name, d->ithing.importAs, d->ithing.pubImport, d->ithing.loc };
				if(auto it = std::find_if(imports.begin(), imports.end(), [&ithing](auto x) -> bool { return x.first.name == ithing.name; });
					it != imports.end())
				{
					SimpleError::make(ithing.loc, "importing previously imported module '%s'", ithing.name)
						->append(SimpleError::make(MsgType::Note, it->first.loc, "previous import was here:"))
						->postAndQuit();
				}

				imports.push_back({ ithing, stree });

				// debuglog("%s depends on %s\n", frontend::getFilenameFromPath(file).c_str(), frontend::getFilenameFromPath(d->name).c_str());
			}

			state->dtrees[file] = sst::typecheck(state, state->parsed[file], imports);
		}

		return state->dtrees[state->fullMainFile];
	}
Exemplo n.º 8
0
	static void runProgramWithJit(llvm::Module* mod)
	{
		// all linked already.
		// dump here, before the output.

		llvm::verifyModule(*mod, &llvm::errs());
		if(mod->getFunction("main") != 0)
		{
			std::string err;
			llvm::ExecutionEngine* ee = llvm::EngineBuilder(std::unique_ptr<llvm::Module>(mod))
						.setErrorStr(&err)
						.setMCJITMemoryManager(llvm::make_unique<llvm::SectionMemoryManager>())
						.create();

			void* func = ee->getPointerToFunction(mod->getFunction("main"));
			iceAssert(func);
			auto mainfunc = (int (*)(int, const char**)) func;

			const char* m[] = { ("__llvmJIT_" + mod->getModuleIdentifier()).c_str() };

			// finalise the object, which causes the memory to be executable
			// f*****g NX bit
			ee->finalizeObject();
			mainfunc(1, m);
		}
		else
		{
			error("no main() function, cannot JIT");
		}
	}
Exemplo n.º 9
0
	void GlobalVariable::setInitialValue(ConstantValue* constVal)
	{
		if(constVal && constVal->getType() != this->getType()->getPointerElementType())
			error("storing value of '%s' in global var of type '%s'", constVal->getType(), this->getType());

		iceAssert((!constVal || constVal->getType() == this->getType()->getPointerElementType()) && "invalid type");
		this->initValue = constVal;
	}
Exemplo n.º 10
0
	fir::Module* generateFIRModule(CollectorState* state, sst::DefinitionTree* maintree)
	{
		iceAssert(maintree && maintree->topLevel);

		for(const auto& dt : state->dtrees)
		{
			for(const auto& def : dt.second->typeDefnMap)
			{
				if(auto it = maintree->typeDefnMap.find(def.first); it != maintree->typeDefnMap.end())
					iceAssert(it->second == def.second);

				maintree->typeDefnMap[def.first] = def.second;
			}
		}

		return cgn::codegen(maintree);
	}
Exemplo n.º 11
0
	Result_t enumerationAccessCodegen(CodegenInstance* cgi, Expr* lhs, Expr* rhs)
	{
		VarRef* enumName = dynamic_cast<VarRef*>(lhs);
		VarRef* caseName = dynamic_cast<VarRef*>(rhs);
		iceAssert(enumName);

		if(!caseName)
			error(rhs, "Expected identifier after enumeration access");

		TypePair_t* tp = cgi->getType(enumName->name);
		iceAssert(tp);
		iceAssert(tp->second.second == TypeKind::Enum);

		Enumeration* enr = dynamic_cast<Enumeration*>(tp->second.first);
		iceAssert(enr);

		Result_t res(0, 0);
		bool found = false;
		for(auto p : enr->cases)
		{
			if(p.first == caseName->name)
			{
				res = p.second->codegen(cgi);
				found = true;
				break;
			}
		}


		if(!found)
			error(rhs, "Enum '%s' has no such case '%s'", enumName->name.c_str(), caseName->name.c_str());

		if(!enr->isStrong)
			return res;


		// strong enum.
		// create a temp alloca, then use GEP to set the value, then return.
		llvm::Value* alloca = cgi->allocateInstanceInBlock(tp->first);
		llvm::Value* gep = cgi->builder.CreateStructGEP(alloca, 0);

		cgi->builder.CreateStore(res.result.first, gep);
		return Result_t(cgi->builder.CreateLoad(alloca), alloca);
	}
Exemplo n.º 12
0
std::string getSpannedContext(const Location& loc, const std::vector<util::ESpan>& spans, size_t* adjust, size_t* num_width, size_t* margin,
	bool underline, bool bottompad, std::string underlineColour)
{
	std::string ret;

	iceAssert(adjust && margin && num_width);
	iceAssert((underline == bottompad || bottompad) && "cannot underline without bottom pad");

	if(!std::is_sorted(spans.begin(), spans.end(), [](auto a, auto b) -> bool { return a.loc.col < b.loc.col; }))
		_error_and_exit("spans must be sorted!");

	*num_width = std::to_string(loc.line + 1).length();

	// one spacing line
	*margin = *num_width + 2;
	ret += strprintf("%s |\n", spaces(*num_width));
	ret += strprintf("%d |%s\n", loc.line + 1, fetchContextLine(loc, adjust));

	if(bottompad)
		ret += strprintf("%s |", spaces(*num_width));

	// ok, now loop through each err, and draw the underline.
	if(underline)
	{
		//* cursor represents the 'virtual' position -- excluding the left margin
		size_t cursor = 0;

		// columns actually start at 1 for some reason.
		ret += spaces(LEFT_PADDING - 1);

		for(auto span : spans)
		{
			// pad out.
			auto tmp = strprintf("%s", spaces(1 + span.loc.col - *adjust - cursor)); cursor += tmp.length();
			ret += tmp + strprintf("%s", span.colour.empty() ? underlineColour : span.colour);

			tmp = strprintf("%s", repeat(UNDERLINE_CHARACTER, span.loc.len)); cursor += tmp.length();
			ret += tmp + strprintf("%s", COLOUR_RESET);
		}
	}

	return ret;
}
Exemplo n.º 13
0
static std::string typestr(MsgType t)
{
	switch(t)
	{
		case MsgType::Note:     return "note";
		case MsgType::Error:    return "error";
		case MsgType::Warning:  return "warning";
		default:                iceAssert(0); return "";
	}
}
Exemplo n.º 14
0
static void checkArray(sst::TypecheckState* fs, DecompMapping* bind, fir::Type* rhs, bool immut)
{
	iceAssert(bind->array);

	if(!rhs->isArrayType() && !rhs->isDynamicArrayType() && !rhs->isArraySliceType() && !rhs->isStringType())
		error(bind->loc, "expected array type in destructuring declaration; found type '%s' instead", rhs);

	if(rhs->isStringType())
	{
		//* note: special-case this, because 1. we want to return chars, but 2. strings are supposed to be immutable.
		for(auto& b : bind->inner)
			checkAndAddBinding(fs, &b, fir::Type::getInt8(), immut, false);

		if(!bind->restName.empty())
		{
			auto fake = util::pool<sst::VarDefn>(bind->loc);

			fake->id = Identifier(bind->restName, IdKind::Name);
			fake->immutable = immut;

			//* note: see typecheck/slices.cpp for mutability rules.
			if(bind->restRef)   fake->type = fir::Type::getCharSlice(sst::getMutabilityOfSliceOfType(rhs));
			else                fake->type = fir::Type::getString();

			fs->stree->addDefinition(bind->restName, fake);

			bind->restDefn = fake;
		}
	}
	else
	{
		for(auto& b : bind->inner)
			checkAndAddBinding(fs, &b, rhs->getArrayElementType(), immut, true);

		if(!bind->restName.empty())
		{
			auto fake = util::pool<sst::VarDefn>(bind->loc);

			fake->id = Identifier(bind->restName, IdKind::Name);
			fake->immutable = immut;

			//* note: see typecheck/slices.cpp for mutability rules.
			if(bind->restRef || rhs->isArraySliceType())
				fake->type = fir::ArraySliceType::get(rhs->getArrayElementType(), sst::getMutabilityOfSliceOfType(rhs));

			else
				fake->type = fir::DynamicArrayType::get(rhs->getArrayElementType());

			fs->stree->addDefinition(bind->restName, fake);
			bind->restDefn = fake;
		}
	}
}
Exemplo n.º 15
0
	std::string typeParamMapToString(const std::string& name, const TypeParamMap_t& map)
	{
		if(map.empty())
			return name;

		std::string ret;
		for(auto m : map)
			ret += (m.first + ":" + m.second->encodedStr()) + ",";

		// shouldn't be empty.
		iceAssert(ret.size() > 0);
		return strprintf("%s<%s>", name, ret.substr(0, ret.length() - 1));
	}
Exemplo n.º 16
0
static void checkTuple(sst::TypecheckState* fs, DecompMapping* bind, fir::Type* rhs, bool immut)
{
	iceAssert(!bind->array);
	if(!rhs->isTupleType())
		error(bind->loc, "expected tuple type in destructuring declaration; found type '%s' instead", rhs);

	auto tty = rhs->toTupleType();
	if(bind->inner.size() != tty->getElementCount())
	{
		error(bind->loc, "too %s bindings in destructuring declaration; expected %d, found %d instead",
			(bind->inner.size() < tty->getElementCount() ? "few" : "many"), tty->getElementCount(), bind->inner.size());
	}

	for(size_t i = 0; i < tty->getElementCount(); i++)
		checkAndAddBinding(fs, &bind->inner[i], tty->getElementN(i), immut, true);
}
Exemplo n.º 17
0
static bool checkBlockPathsReturn(sst::TypecheckState* fs, sst::Block* block, fir::Type* retty, std::vector<sst::Block*>* faulty)
{
	// return value is whether or not the block had a return value;
	// true if all paths explicitly returned, false if not
	// this return value is used to determine whether we need to insert a
	// 'return void' thing.

	bool ret = false;
	for(size_t i = 0; i < block->statements.size(); i++)
	{
		auto& s = block->statements[i];
		if(auto retstmt = dcast(sst::ReturnStmt, s))
		{
			// ok...
			ret = true;
			auto t = retstmt->expectedType;
			iceAssert(t);

			if(fir::getCastDistance(t, retty) < 0)
			{
				if(retstmt->expectedType->isVoidType())
				{
					error(retstmt, "expected value after 'return'; function return type is '%s'", retty);
				}
				else
				{
					std::string msg;
					if(block->isSingleExpr) msg = "invalid single-expression with type '%s' in function returning '%s'";
					else                    msg = "mismatched type in return statement; function returns '%s', value has type '%s'";

					SpanError::make(SimpleError::make(retstmt->loc, msg.c_str(), retty, retstmt->expectedType))
						->add(util::ESpan(retstmt->value->loc, strprintf("type '%s'", retstmt->expectedType)))
						->append(SimpleError::make(MsgType::Note, fs->getCurrentFunction()->loc, "function definition is here:"))
						->postAndQuit();
				}
			}

			if(i != block->statements.size() - 1)
			{
				SimpleError::make(block->statements[i + 1]->loc, "unreachable code after return statement")
					->append(SimpleError::make(MsgType::Note, retstmt->loc, "return statement was here:"))
					->postAndQuit();;

				doTheExit();
			}
		}
		else /* if(i == block->statements.size() - 1) */
		{
			//* it's our duty to check the internals of these things regardless of their exhaustiveness
			//* so that we can check for the elision of the merge block.
			//? eg: if 's' itself does not have an else case, but say one of its branches is exhaustive (all arms return),
			//? then we can elide the merge block for that branch, even though we can't for 's' itself.
			//* this isn't strictly necessary (the program is still correct without it), but we generate nicer IR this way.

			bool exhausted = false;
			if(auto ifstmt = dcast(sst::IfStmt, s); ifstmt)
			{
				bool all = true;
				for(const auto& c: ifstmt->cases)
					all = all && checkBlockPathsReturn(fs, c.body, retty, faulty);

				exhausted = all && ifstmt->elseCase && checkBlockPathsReturn(fs, ifstmt->elseCase, retty, faulty);
				ifstmt->elideMergeBlock = exhausted;
			}
			else if(auto whileloop = dcast(sst::WhileLoop, s); whileloop)
			{
				exhausted = checkBlockPathsReturn(fs, whileloop->body, retty, faulty) && whileloop->isDoVariant;
				whileloop->elideMergeBlock = exhausted;
			}
			else if(auto feloop = dcast(sst::ForeachLoop, s); feloop)
			{
				// we don't set 'exhausted' here beacuse for loops cannot be guaranteed to be exhaustive.
				// but we still want to check the block inside for elision.
				feloop->elideMergeBlock = checkBlockPathsReturn(fs, feloop->body, retty, faulty);
			}

			ret = exhausted;
		}
Exemplo n.º 18
0
static void checkArray(cgn::CodegenState* cs, const DecompMapping& bind, CGResult rhs)
{
	iceAssert(bind.array);

	auto rt = rhs.value->getType();
	bool shouldSliceBeMutable = sst::getMutabilityOfSliceOfType(rt);

	if(!rt->isArrayType() && !rt->isDynamicArrayType() && !rt->isArraySliceType() && !rt->isStringType())
		error(bind.loc, "Expected array type in destructuring declaration; found type '%s' instead", rt);

	if(rt->isStringType())
	{
		// do a bounds check.
		auto numbinds = fir::ConstantInt::getInt64(bind.inner.size());
		{
			auto checkf = cgn::glue::string::getBoundsCheckFunction(cs, true);
			iceAssert(checkf);

			auto strloc = fir::ConstantString::get(bind.loc.toString());
			cs->irb.Call(checkf, cs->irb.GetSAALength(rhs.value), numbinds, strloc);
		}

		//* note: special-case this, because 1. we want to return chars
		auto strdat = cs->irb.PointerTypeCast(cs->irb.GetSAAData(rhs.value), fir::Type::getMutInt8Ptr());
		{
			size_t idx = 0;
			for(auto& b : bind.inner)
			{
				auto v = CGResult(cs->irb.ReadPtr(cs->irb.PointerAdd(strdat, fir::ConstantInt::getInt64(idx))));
				cs->generateDecompositionBindings(b, v, false);

				idx++;
			}
		}

		if(!bind.restName.empty())
		{
			if(bind.restRef)
			{
				// make a slice of char.
				auto remaining = cs->irb.Subtract(cs->irb.GetSAALength(rhs.value), numbinds);

				auto slice = cs->irb.CreateValue(fir::Type::getCharSlice(shouldSliceBeMutable));
				slice = cs->irb.SetArraySliceData(slice, cs->irb.PointerAdd(strdat, numbinds));
				slice = cs->irb.SetArraySliceLength(slice, remaining);

				handleDefn(cs, bind.restDefn, CGResult(slice));
			}
			else
			{
				// make string.
				// auto remaining = cs->irb.Subtract(cs->irb.GetSAALength(rhs.value), numbinds);

				auto clonef = cgn::glue::string::getCloneFunction(cs);
				iceAssert(clonef);

				auto string = cs->irb.Call(clonef, rhs.value, numbinds);

				handleDefn(cs, bind.restDefn, CGResult(string));
			}
		}
	}
	else
	{
		auto array = rhs.value;
		fir::Value* arrlen = 0;

		auto numbinds = fir::ConstantInt::getInt64(bind.inner.size());
		{
			//* note: 'true' means we're performing a decomposition, so print a more appropriate error message on bounds failure.
			auto checkf = cgn::glue::array::getBoundsCheckFunction(cs, true);
			iceAssert(checkf);

			if(rt->isArrayType())               arrlen = fir::ConstantInt::getInt64(rt->toArrayType()->getArraySize());
			else if(rt->isArraySliceType())     arrlen = cs->irb.GetArraySliceLength(array);
			else if(rt->isDynamicArrayType())   arrlen = cs->irb.GetSAALength(array);
			else                                iceAssert(0);

			auto strloc = fir::ConstantString::get(bind.loc.toString());
			cs->irb.Call(checkf, arrlen, numbinds, strloc);
		}

		// # if 0
		if(!rhs->islorclvalue() && rt->isArrayType())
		{
			//* because of the way LLVM is designed, and hence by extension how we are designed,
			//* fixed-sized arrays are kinda dumb. If we don't have a pointer to the array (for whatever reason???),
			//* then we can't do a GEP access, and hence can't get a pointer to use for the 'rest' binding. So,
			//* we error on that case but allow binding the rest.

			//* theoretically if the compiler is well designed we should never hit this case, but who knows?

			size_t idx = 0;
			for(auto& b : bind.inner)
			{
				auto v = CGResult(cs->irb.ExtractValue(array, { idx }));
				cs->generateDecompositionBindings(b, v, false);

				idx++;
			}

			warn(bind.loc, "Destructure of array without pointer (shouldn't happen!)");
			if(!bind.restName.empty())
				error(bind.loc, "Could not get pointer to array (of type '%s') to create binding for '...'", rt);
		}
		else
		// #endif

		{
			fir::Value* data = 0;

			if(rt->isArrayType())               data = cs->irb.ConstGEP2(rhs.value, 0, 0);
			else if(rt->isArraySliceType())     data = cs->irb.GetArraySliceData(array);
			else if(rt->isDynamicArrayType())   data = cs->irb.GetSAAData(array);
			else                                iceAssert(0);


			size_t idx = 0;
			for(auto& b : bind.inner)
			{
				auto ptr = cs->irb.PointerAdd(data, fir::ConstantInt::getInt64(idx));

				auto v = CGResult(cs->irb.Dereference(ptr));
				cs->generateDecompositionBindings(b, v, true);

				idx++;
			}

			if(!bind.restName.empty())
			{
				if(bind.restRef)
				{
					auto sty = fir::ArraySliceType::get(rt->getArrayElementType(), shouldSliceBeMutable);

					auto remaining = cs->irb.Subtract(arrlen, numbinds);

					auto slice = cs->irb.CreateValue(sty);
					slice = cs->irb.SetArraySliceData(slice, cs->irb.PointerAdd(data, numbinds));
					slice = cs->irb.SetArraySliceLength(slice, remaining);

					handleDefn(cs, bind.restDefn, CGResult(slice));
				}
				else
				{
					// always return a dynamic array here.
					//* note: in order to make our lives somewhat easier, for fixed arrays, we create a fake slice pointing to its data, then we
					//* call clone on that instead.

					fir::Value* clonee = 0;
					if(rt->isArrayType())
					{
						clonee = cs->irb.CreateValue(fir::ArraySliceType::get(rt->getArrayElementType(), shouldSliceBeMutable));
						clonee = cs->irb.SetArraySliceData(clonee, data);
						clonee = cs->irb.SetArraySliceLength(clonee, fir::ConstantInt::getInt64(rt->toArrayType()->getArraySize()));
					}
					else
					{
						clonee = array;
					}

					auto clonef = cgn::glue::array::getCloneFunction(cs, clonee->getType());
					iceAssert(clonef);

					auto ret = cs->irb.Call(clonef, clonee, numbinds);

					handleDefn(cs, bind.restDefn, CGResult(ret));
				}
			}
		}
	}
}
Exemplo n.º 19
0
	static void doGlobalConstructors(std::string filename, CompiledData& data, Ast::Root* root, llvm::Module* mod)
	{
		auto& rootmap = std::get<2>(data);

		bool needGlobalConstructor = false;
		if(root->globalConstructorTrampoline != 0) needGlobalConstructor = true;
		for(auto pair : std::get<2>(data))
		{
			if(pair.second->globalConstructorTrampoline != 0)
			{
				needGlobalConstructor = true;
				break;
			}
		}


		if(needGlobalConstructor)
		{
			std::vector<llvm::Function*> constructors;
			rootmap[filename] = root;

			for(auto pair : rootmap)
			{
				if(pair.second->globalConstructorTrampoline != 0)
				{
					llvm::Function* constr = mod->getFunction(pair.second->globalConstructorTrampoline->getName());
					if(!constr)
					{
						error("required global constructor %s was not found in the module!",
							pair.second->globalConstructorTrampoline->getName().c_str());
					}
					else
					{
						constructors.push_back(constr);
					}
				}
			}

			rootmap.erase(filename);

			llvm::FunctionType* ft = llvm::FunctionType::get(llvm::Type::getVoidTy(llvm::getGlobalContext()), false);
			llvm::Function* gconstr = llvm::Function::Create(ft, llvm::GlobalValue::ExternalLinkage,
				"__global_constructor_top_level__", mod);

			llvm::IRBuilder<> builder(llvm::getGlobalContext());

			llvm::BasicBlock* iblock = llvm::BasicBlock::Create(llvm::getGlobalContext(), "initialiser", gconstr);
			builder.SetInsertPoint(iblock);

			for(auto f : constructors)
			{
				iceAssert(f);
				builder.CreateCall(f);
			}

			builder.CreateRetVoid();


			if(!Compiler::getNoAutoGlobalConstructor())
			{
				// insert a call at the beginning of main().
				llvm::Function* mainfunc = mod->getFunction("main");
				iceAssert(mainfunc);

				llvm::BasicBlock* entry = &mainfunc->getEntryBlock();
				llvm::BasicBlock* f = llvm::BasicBlock::Create(llvm::getGlobalContext(), "__main_entry", mainfunc);

				f->moveBefore(entry);
				builder.SetInsertPoint(f);
				builder.CreateCall(gconstr);
				builder.CreateBr(entry);
			}
		}
	}