diff --git a/BeefLibs/corlib/src/WebAssembly.bf b/BeefLibs/corlib/src/WebAssembly.bf new file mode 100644 index 00000000..5ae2ee81 --- /dev/null +++ b/BeefLibs/corlib/src/WebAssembly.bf @@ -0,0 +1,134 @@ +#if BF_PLATFORM_WASM + +namespace System; + +using System.Interop; + +class WebAssembly +{ + [CLink] + private static extern int32 emscripten_asm_const_int(char8* code, char8* arg_sigs, ...); + [CLink] + private static extern void emscripten_asm_const_ptr(char8* code, char8* arg_sigs, ...); + [CLink] + private static extern double emscripten_asm_const_double(char8* code, char8* arg_sigs, ...); + + [Intrinsic(":add_string_to_section")] + private static extern char8* add_string_to_section(uint8* string, uint8* section); + + private static Span GetStringData(String value) + { + return StringView(value).ToRawData(); + } + + /** + * Returns the string added to the specified data section. + */ + private static char8* AddStringToSection(T0 value, T1 section) where T0 : const String where T1 : const String + { + static uint8[?] data = GetStringData(value); + static uint8[?] sectionStr = GetStringData(section); + + #unwarn + return add_string_to_section(&data, §ionStr); + } + + private static void GetArgSigInternal(Type t, String s) + { + switch(t) + { + case typeof(float): + s.Append('f'); + case typeof(double): + s.Append('d'); + case typeof(c_ulong): fallthrough; + case typeof(c_ulonglong): fallthrough; + case typeof(c_longlong): fallthrough; + case typeof(c_long): + s.Append('j'); + default: +#if BF_32_BIT + s.Append('i'); +#else + s.Append('p'); +#endif + } + } + + [Comptime] + static void JSGetArgSig(Type t, String s) + { + if(t.IsTuple) + { + int count = t.FieldCount; + for(int i = 0; i < count; i++) + { + var type = t.GetField(i).Get().FieldType; + GetArgSigInternal(type, s); + } + }else + GetArgSigInternal(t, s); + } + + private static String JSGetResultName(Type t) + { + if(t.IsPointer) + return "ptr"; + else if (t.IsFloatingPoint) + return "double"; + else + return "int"; + } + + [Comptime] + private static void GetArgString(String s) + { + Type type = typeof(T); + int fieldCount = 0; + if (type.IsTuple) + { + fieldCount = type.FieldCount; + for(int i = 0; i< fieldCount; i++) + { + if(i == fieldCount-1) + s.AppendF("p0.{}", i); + else + s.AppendF("p0.{}, ", i); + } + } + else + s.Append("p0"); + } + + private static String JSGetCallString() where TCall : const String + { + if (TCall == null) + return ""; + var argSigs = JSGetArgSig(typeof(T0), .. scope .()); + var argString = GetArgString(.. scope .()); + return new $"result = emscripten_asm_const_{JSGetResultName(typeof(TResult))}(AddStringToSection({TCall.Quote(.. scope .())}, \"em_asm\"), \"{argSigs}\", {argString});"; + } + + private static String JSGetCallString() where TCall : const String + { + if (TCall == null) + return ""; + return new $"result = emscripten_asm_const_{JSGetResultName(typeof(TResult))}(AddStringToSection({TCall.Quote(.. scope .())}, \"em_asm\"), \"i\");"; + } + + public static TResult JSCall(TCall callString, T0 p0) where TCall : const String + { + TResult result = default; + Compiler.Mixin(JSGetCallString()); + return result; + } + + public static TResult JSCall(TCall callString) where TCall : const String + { + TResult result = default; + Compiler.Mixin(JSGetCallString()); + return result; + } +} + +#endif diff --git a/IDEHelper/Compiler/BfIRCodeGen.cpp b/IDEHelper/Compiler/BfIRCodeGen.cpp index 15315440..b3960e6f 100644 --- a/IDEHelper/Compiler/BfIRCodeGen.cpp +++ b/IDEHelper/Compiler/BfIRCodeGen.cpp @@ -3075,11 +3075,60 @@ void BfIRCodeGen::HandleNextCmd() switch (intrinsicData->mIntrinsic) { case BfIRIntrinsic__PLATFORM: + { + if (intrinsicData->mName == "add_string_to_section") { - FatalError(StrFormat("Unable to find intrinsic '%s'", intrinsicData->mName.c_str())); - } - break; + llvm::StringRef strContent[2]; + llvm::ConstantDataArray* dataArray; + for (int i = 0; i < 2; i++) + { + if (const llvm::ConstantExpr* ce = llvm::dyn_cast(args[i])) + { + llvm::Value* firstOperand = ce->getOperand(0); + if (llvm::GlobalVariable* gv = llvm::dyn_cast(firstOperand)) + { + if (gv->getType()->isPointerTy()) + { + if (dataArray = llvm::dyn_cast(gv->getInitializer())) + strContent[i] = dataArray->getAsString(); + } + } + } + else + FatalError("Value is not ConstantExpr"); + } + + static int symbolCount = 0; + symbolCount++; + + auto charType = llvm::IntegerType::get(*mLLVMContext, 8); + std::vector chars(strContent[0].size()); + for (unsigned int i = 0; i < strContent[0].size(); i++) + { + chars[i] = llvm::ConstantInt::get(charType, strContent[0][i]);; + } + + chars.push_back(llvm::ConstantInt::get(charType, 0)); + auto stringType = llvm::ArrayType::get(charType, chars.size()); + + std::string symbolName = strContent[1].str() + "_" + std::to_string(symbolCount); + llvm::StringRef resultStringRef(symbolName); + + auto globalVar = (llvm::GlobalVariable*)mLLVMModule->getOrInsertGlobal(symbolName, stringType); + globalVar->setSection(strContent[1]); + globalVar->setInitializer(llvm::ConstantArray::get(stringType, chars)); + globalVar->setConstant(true); + globalVar->setLinkage(llvm::GlobalValue::LinkageTypes::ExternalLinkage); + globalVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); + + SetResult(curId, llvm::ConstantExpr::getBitCast(globalVar, charType->getPointerTo())); + break; + } + + FatalError(StrFormat("Unable to find intrinsic '%s'", intrinsicData->mName.c_str())); + break; + } case BfIRIntrinsic_Add: case BfIRIntrinsic_And: case BfIRIntrinsic_Div: