* @file
*
* This file implements codegen for CHIR Spawn creation.
*/
#include "Base/SpawnExprImpl.h"
#include "Base/CHIRExprWrapper.h"
#include "CGModule.h"
#include "IRBuilder.h"
#include "Utils/CGUtils.h"
#include "cangjie/CHIR/IR/Expression/Terminator.h"
#include "cangjie/CHIR/IR/Type/ClassDef.h"
#include "cangjie/CHIR/IR/Type/Type.h"
#include "cangjie/CHIR/IR/Value/Value.h"
using namespace Cangjie;
using namespace CodeGen;
namespace {
void GenerateCheckSpawnResult(IRBuilder2& irBuilder, llvm::Value* result)
{
#ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND
auto i8PtrTy = irBuilder.getInt8PtrTy();
auto failure = llvm::ConstantPointerNull::get(i8PtrTy);
auto compare = irBuilder.CreateICmpEQ(result, failure);
auto [throwBB, endBB] =
Vec2Tuple<2>(irBuilder.CreateAndInsertBasicBlocks({GenNameForBB("if.throw"), GenNameForBB("if.end")}));
(void)irBuilder.CreateCondBr(compare, throwBB, endBB);
irBuilder.SetInsertPoint(throwBB);
irBuilder.CreateSpawnException();
irBuilder.CreateUnreachable();
irBuilder.SetInsertPoint(endBB);
#endif
}
llvm::Value* GenerateSpawnWithExecuteFuture(IRBuilder2& irBuilder, const CHIRSpawnWrapper& spawn)
{
auto& cgMod = irBuilder.GetCGModule();
auto futureObj = cgMod | spawn.GetFuture();
std::optional<CGValue*> threadCtx = std::nullopt;
if (spawn.GetSpawnArg()) {
threadCtx = cgMod | spawn.GetSpawnArg();
}
auto methods =
StaticCast<CHIR::ClassType*>(spawn.GetFuture()->GetType()->GetTypeArgs()[0])->GetClassDef()->GetMethods();
auto it = std::find_if(
methods.begin(), methods.end(), [](auto method) { return method->GetSrcCodeIdentifier() == "execute"; });
CJC_ASSERT_WITH_MSG(it != methods.end(), "The execute function is not found in Future.");
auto cgFunc = cgMod.GetOrInsertCGFunction(*it);
CJC_NULLPTR_CHECK(cgFunc);
auto cjThreadHandle = irBuilder.CallSpawnIntrinsic(*futureObj, *cgFunc, threadCtx, true);
CJC_NULLPTR_CHECK(cjThreadHandle);
GenerateCheckSpawnResult(irBuilder, cjThreadHandle);
#ifdef CANGJIE_CODEGEN_CJNATIVE_BACKEND
auto threadObj =
irBuilder.CreateLoad(irBuilder.getInt8PtrTy(1), irBuilder.CreateGEP(*futureObj, {0}).GetRawValue());
auto& llvmCtx = irBuilder.GetLLVMContext();
auto tiNullVal = llvm::ConstantPointerNull::get(CGType::GetOrCreateTypeInfoPtrType(llvmCtx));
irBuilder.CreateSetRuntimeCJThreadHandleCall(
{irBuilder.CreateEntryAlloca(CGType::GetUnitType(llvmCtx)), threadObj, cjThreadHandle, tiNullVal});
#endif
return futureObj->GetRawValue();
}
llvm::Value* GenerateSpawnWithExecuteClosure(IRBuilder2& irBuilder, const CHIRSpawnWrapper& spawn)
{
auto& cgMod = irBuilder.GetCGModule();
auto closureNode = spawn.GetClosure();
auto closure = cgMod | closureNode;
std::optional<CGValue*> threadCtx = std::nullopt;
if (spawn.GetSpawnArg()) {
threadCtx = cgMod | spawn.GetSpawnArg();
}
CJC_NULLPTR_CHECK(spawn.GetExecuteClosure());
auto cgFunc = cgMod.GetOrInsertCGFunction(spawn.GetExecuteClosure());
auto result = irBuilder.CallSpawnIntrinsic(*closure, *cgFunc, threadCtx,
false, irBuilder.CreateTypeInfo(*spawn.GetResult()->GetType()));
CJC_NULLPTR_CHECK(result);
GenerateCheckSpawnResult(irBuilder, result);
return nullptr;
}
}
* Generate the spawn expression.
* A spawn expression has two types of CHIR node shapes.
* 1. Spawn(thunk { EXECUTE[ futureObj, threadCtx? ] })
* Create a thread and pass a future object as the argument,
* the thread entry function is `Future.execute`.
* 2. Spawn(thunk { EXECUTE_CLOSURE[ closure, threadCtx? ] })
* Create a thread and pass a closure as the argument,
* the thread entry function is `Future.executeClosure`.
* A spawn expression may contains a thread context, like `spawn(ctx) { ... }`.
*/
llvm::Value* CodeGen::GenerateSpawn(IRBuilder2& irBuilder, const CHIRSpawnWrapper& spawn)
{
if (!spawn.IsExecuteClosure()) {
return GenerateSpawnWithExecuteFuture(irBuilder, spawn);
} else {
return GenerateSpawnWithExecuteClosure(irBuilder, spawn);
}
}