`/p:DisableCrossgen=true` - Skips building the installer if you don't need it (builds faster)
-`-p:KeepNativeSymbols=true` - Keep the symbols in the binary instead of stripping them out to a separate file. This helps with debugging Mono with lldb.
+`/p:KeepNativeSymbols=true` - Keep the symbols in the binary instead of stripping them out to a separate file. This helps with debugging Mono with lldb.
The build has a number of options that you can learn about using build -?.
char *llvm_outfile;
char *data_outfile;
char *export_symbols_outfile;
- char *compiled_methods_outfile;
+ char *trimming_eligible_methods_outfile;
GList *profile_files;
GList *mibc_profile_files;
gboolean save_temps;
FILE *logfile;
FILE *instances_logfile;
FILE *data_outfile;
- FILE *compiled_methods_outfile;
+ FILE *trimming_eligible_methods_outfile;
int datafile_offset;
int gc_name_offset;
opts->llvm_outfile = g_strdup (arg + strlen ("llvm-outfile="));
} else if (str_begins_with (arg, "export-symbols-outfile=")) {
opts->export_symbols_outfile = g_strdup (arg + strlen ("export-symbols-outfile="));
- } else if (str_begins_with (arg, "compiled-methods-outfile=")) {
- opts->compiled_methods_outfile = g_strdup (arg + strlen ("compiled-methods-outfile="));
+ } else if (str_begins_with (arg, "trimming-eligible-methods-outfile=")) {
+ opts->trimming_eligible_methods_outfile = g_strdup (arg + strlen ("trimming-eligible-methods-outfile="));
} else if (str_begins_with (arg, "temp-path=")) {
opts->temp_path = clean_path (g_strdup (arg + strlen ("temp-path=")));
} else if (str_begins_with (arg, "save-temps")) {
mono_atomic_inc_i32 (&acfg->stats.ccount);
- if (acfg->aot_opts.compiled_methods_outfile && acfg->compiled_methods_outfile != NULL) {
- if (!mono_method_is_generic_impl (method) && method->token != 0) {
- fprintf (acfg->compiled_methods_outfile, "%x\n", method->token);
+ if (acfg->aot_opts.trimming_eligible_methods_outfile && acfg->trimming_eligible_methods_outfile != NULL) {
+ if (!mono_method_is_generic_impl (method) && method->token != 0 && !cfg->deopt && !cfg->interp_entry_only && mini_get_interp_callbacks ()->jit_call_can_be_supported (method, mono_method_signature_internal (method), acfg->aot_opts.llvm_only)) {
+ // The call back to jit_call_can_be_supported is necessary for WASM, because it would still interprete some methods sometimes even though they were already AOT'ed.
+ // When that happens, interpreter needs to have the capability to call the AOT'ed version of that method, since the method body has already been trimmed.
+ fprintf (acfg->trimming_eligible_methods_outfile, "%x\n", method->token);
}
}
}
acfg->logfile = fopen (acfg->aot_opts.logfile, "a+");
}
- if (acfg->aot_opts.compiled_methods_outfile && acfg->dedup_phase != DEDUP_COLLECT) {
- acfg->compiled_methods_outfile = fopen (acfg->aot_opts.compiled_methods_outfile, "w+");
- if (!acfg->compiled_methods_outfile)
- aot_printerrf (acfg, "Unable to open compiled-methods-outfile specified file %s\n", acfg->aot_opts.compiled_methods_outfile);
+ if (acfg->aot_opts.trimming_eligible_methods_outfile && acfg->dedup_phase != DEDUP_COLLECT) {
+ acfg->trimming_eligible_methods_outfile = fopen (acfg->aot_opts.trimming_eligible_methods_outfile, "w+");
+ if (!acfg->trimming_eligible_methods_outfile)
+ aot_printerrf (acfg, "Unable to open trimming-eligible-methods-outfile specified file %s\n", acfg->aot_opts.trimming_eligible_methods_outfile);
else {
- fprintf(acfg->compiled_methods_outfile, "%s\n", ass->image->filename);
- fprintf(acfg->compiled_methods_outfile, "%s\n", ass->image->guid);
+ fprintf(acfg->trimming_eligible_methods_outfile, "%s\n", ass->image->filename);
+ fprintf(acfg->trimming_eligible_methods_outfile, "%s\n", ass->image->guid);
}
}
current_acfg = NULL;
- if (acfg->compiled_methods_outfile) {
- fclose (acfg->compiled_methods_outfile);
+ if (acfg->trimming_eligible_methods_outfile) {
+ fclose (acfg->trimming_eligible_methods_outfile);
}
return emit_aot_image (acfg);
dedup_methods = g_hash_table_new (NULL, NULL);
}
- if (aot_opts.compiled_methods_outfile) {
- if (g_ensure_directory_exists (aot_opts.compiled_methods_outfile) == FALSE) {
- fprintf (stderr, "AOT : failed to create the directory to save the compiled method names: %s\n", aot_opts.compiled_methods_outfile);
+ if (aot_opts.trimming_eligible_methods_outfile) {
+ if (g_ensure_directory_exists (aot_opts.trimming_eligible_methods_outfile) == FALSE) {
+ fprintf (stderr, "AOT : failed to create the directory to save the trimmable method names: %s\n", aot_opts.trimming_eligible_methods_outfile);
exit (1);
}
}
MONO_EE_CALLBACK (void, entry_llvmonly, (gpointer res, gpointer *args, gpointer imethod)) \
MONO_EE_CALLBACK (gpointer, get_interp_method, (MonoMethod *method)) \
MONO_EE_CALLBACK (MonoJitInfo*, compile_interp_method, (MonoMethod *method, MonoError *error)) \
+ MONO_EE_CALLBACK (gboolean, jit_call_can_be_supported, (MonoMethod *method, MonoMethodSignature *sig, gboolean is_llvm_only)) \
typedef struct _MonoEECallbacks {
return NULL;
}
+static gboolean
+stub_jit_call_can_be_supported (MonoMethod *method, MonoMethodSignature *sig, gboolean is_llvm_only)
+{
+ return TRUE;
+}
+
#undef MONO_EE_CALLBACK
#define MONO_EE_CALLBACK(ret, name, sig) stub_ ## name,
int
mono_interp_type_size (MonoType *type, int mt, int *align_p);
+gboolean
+interp_jit_call_can_be_supported (MonoMethod *method, MonoMethodSignature *sig, gboolean is_llvm_only);
+
#if HOST_BROWSER
gboolean
}
ip += 5;
- goto call;
+ InterpMethodCodeType code_type = cmethod->code_type;
+
+ g_assert (code_type == IMETHOD_CODE_UNKNOWN ||
+ code_type == IMETHOD_CODE_INTERP ||
+ code_type == IMETHOD_CODE_COMPILED);
+
+ if (G_UNLIKELY (code_type == IMETHOD_CODE_UNKNOWN)) {
+ // FIXME push/pop LMF
+ MonoMethodSignature *sig = mono_method_signature_internal (cmethod->method);
+ if (mono_interp_jit_call_supported (cmethod->method, sig))
+ code_type = IMETHOD_CODE_COMPILED;
+ else
+ code_type = IMETHOD_CODE_INTERP;
+ cmethod->code_type = code_type;
+ }
+
+ if (code_type == IMETHOD_CODE_INTERP) {
+
+ goto call;
+
+ } else if (code_type == IMETHOD_CODE_COMPILED) {
+ frame->state.ip = ip;
+ error_init_reuse (error);
+ do_jit_call (context, (stackval*)(locals + return_offset), (stackval*)(locals + call_args_offset), frame, cmethod, error);
+ if (!is_ok (error)) {
+ MonoException *call_ex = interp_error_convert_to_exception (frame, error, ip);
+ THROW_EX (call_ex, ip);
+ }
+
+ CHECK_RESUME_STATE (context);
+ }
+
+ MINT_IN_BREAK;
}
MINT_IN_CASE(MINT_CALLI) {
gboolean need_unbox;
return (context->stack_pointer + size) < (context->stack_start + INTERP_STACK_SIZE);
}
+gboolean
+interp_jit_call_can_be_supported (MonoMethod *method, MonoMethodSignature *sig, gboolean is_llvm_only)
+{
+ if (sig->param_count > 10)
+ return FALSE;
+ if (sig->pinvoke)
+ return FALSE;
+ if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
+ return FALSE;
+ if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
+ return FALSE;
+ if (!is_llvm_only && method->is_inflated)
+ return FALSE;
+ if (method->string_ctor)
+ return FALSE;
+ if (method->wrapper_type != MONO_WRAPPER_NONE)
+ return FALSE;
+
+ if (method->flags & METHOD_ATTRIBUTE_REQSECOBJ)
+ /* Used to mark methods containing StackCrawlMark locals */
+ return FALSE;
+
+ return TRUE;
+}
+
static void
interp_cleanup (void)
{
{
GSList *l;
- if (sig->param_count > 10)
- return FALSE;
- if (sig->pinvoke)
- return FALSE;
- if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
- return FALSE;
- if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
- return FALSE;
- if (!mono_llvm_only && method->is_inflated)
- return FALSE;
- if (method->string_ctor)
- return FALSE;
- if (method->wrapper_type != MONO_WRAPPER_NONE)
- return FALSE;
-
- if (method->flags & METHOD_ATTRIBUTE_REQSECOBJ)
- /* Used to mark methods containing StackCrawlMark locals */
+ if (!interp_jit_call_can_be_supported (method, sig, mono_llvm_only))
return FALSE;
if (mono_aot_only && m_class_get_image (method->klass)->aot_module && !(method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) {
int nargs = csignature->param_count + !!csignature->hasthis;
InterpInst *prev_last_ins;
+ if (header->code_size == 0)
+ /* IL stripped */
+ return FALSE;
+
if (csignature->is_inflated)
generic_context = mono_method_get_context (target_method);
else {
LibraryFormat="$(_AotLibraryFormat)"
Assemblies="@(AotInputAssemblies)"
OutputDir="$(PublishDir)"
- CollectCompiledMethods="$(StripILCode)"
- CompiledMethodsOutputDirectory="$(CompiledMethodsOutputDirectory)"
+ CollectTrimmingEligibleMethods="$(StripILCode)"
+ TrimmingEligibleMethodsOutputDirectory="$(TrimmingEligibleMethodsOutputDirectory)"
IntermediateOutputPath="$(IntermediateOutputPath)"
UseAotDataFile="$(UseAotDataFile)"
MibcProfilePath="$(MibcProfilePath)"
AOT?=false
USE_LLVM?=false
StripILCode?=false
-CompiledMethodsOutputDirectory?= #<path-to-a-writable-directory>
+TrimmingEligibleMethodsOutputDirectory?= #<path-to-a-writable-directory>
#MIBC_PROFILE_PATH=<path-to-mibc-for-sample>
/p:RunAOTCompilation=$(AOT) \
/p:MonoEnableLLVM=$(USE_LLVM) \
/p:StripILCode=$(StripILCode) \
- /p:CompiledMethodsOutputDirectory=$(CompiledMethodsOutputDirectory) \
+ /p:TrimmingEligibleMethodsOutputDirectory=$(TrimmingEligibleMethodsOutputDirectory) \
'/p:MibcProfilePath="$(MIBC_PROFILE_PATH)"' \
/bl
<Project>
<!-- This depends on the root Directory.Build.targets imported this file -->
<UsingTask TaskName="MonoAOTCompiler" AssemblyFile="$(MonoAOTCompilerTasksAssemblyPath)" />
+ <UsingTask TaskName="ILStrip" AssemblyFile="$(MonoTargetsTasksAssemblyPath)" />
<UsingTask TaskName="RuntimeConfigParserTask" AssemblyFile="$(MonoTargetsTasksAssemblyPath)" />
<!-- TODO: this breaks runtime tests on Helix due to the file not being there for some reason. Once this is fixed we can remove the UpdateRuntimePack target here -->
CacheFilePath="$(_AOTCompilerCacheFile)"
LLVMDebug="dwarfdebug"
LLVMPath="$(EmscriptenUpstreamBinPath)"
+ CollectTrimmingEligibleMethods="$(WasmStripILAfterAOT)"
+ TrimmingEligibleMethodsOutputDirectory="$(_WasmIntermediateOutputPath)tokens"
IntermediateOutputPath="$(_WasmIntermediateOutputPath)"
AotProfilePath="@(AotProfilePath)">
<Output TaskParameter="CompiledAssemblies" ItemName="_WasmAssembliesInternal" />
<Output TaskParameter="FileWrites" ItemName="FileWrites" />
</MonoAOTCompiler>
+ <ILStrip
+ Condition=" '$(WasmStripILAfterAOT)' == 'true' "
+ TrimIndividualMethods="true"
+ Assemblies="@(_WasmAssembliesInternal)">
+ <Output TaskParameter="TrimmedAssemblies" ItemName="_ILStripTrimmedAssemblies" />
+ </ILStrip>
+ <Move
+ Condition=" '$(WasmStripILAfterAOT)' == 'true' "
+ SourceFiles="@(_ILStripTrimmedAssemblies->'%(TrimmedAssemblyFileName)')"
+ DestinationFiles="@(_ILStripTrimmedAssemblies)"
+ />
<ItemGroup>
<_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" />
- AppBundle directly contains user files
- AppBundle/_framework contains generated files (dlls, runtime scripts, icu)
- AppBundle/_content contains web files from nuget packages (css, js, etc)
+ - $(WasmStripILAfterAOT) - Set to true to enable trimming away AOT compiled methods body (IL code)
+ Defaults to false.
Public items:
- @(WasmExtraFilesToDeploy) - Files to copy to $(WasmAppDir).
<WasmAssemblyExtension Condition="'$(WasmEnableWebcil)' == 'true'">.wasm</WasmAssemblyExtension>
<WasmAssemblyExtension Condition="'$(WasmEnableWebcil)' != 'true'">.dll</WasmAssemblyExtension>
+ <!-- by default, method body IL code trimming is disabled -->
+ <WasmStripILAfterAOT Condition="'$(WasmStripILAfterAOT)' == ''">false</WasmStripILAfterAOT>
+
<WasmRuntimeAssetsLocation Condition="'$(WasmRuntimeAssetsLocation)' == ''">_framework</WasmRuntimeAssetsLocation>
</PropertyGroup>
/// - LlvmObjectFile (if using LLVM)
/// - LlvmBitcodeFile (if using LLVM-only)
/// - ExportsFile (used in LibraryMode only)
- /// - MethodTokenFile (when using CollectCompiledMethods=true)
+ /// - MethodTokenFile (when using CollectTrimmingEligibleMethods=true)
/// </summary>
[Output]
public ITaskItem[]? CompiledAssemblies { get; set; }
/// <summary>
/// Instructs the AOT compiler to print the list of aot compiled methods
/// </summary>
- public bool CollectCompiledMethods { get; set; }
+ public bool CollectTrimmingEligibleMethods { get; set; }
/// <summary>
- /// Directory to store the aot output when using switch compiled-methods-outfile
+ /// Directory to store the aot output when using switch trimming-eligible-methods-outfile
/// </summary>
- public string? CompiledMethodsOutputDirectory { get; set; }
+ public string? TrimmingEligibleMethodsOutputDirectory { get; set; }
/// <summary>
/// File to use for profile-guided optimization, *only* the methods described in the file will be AOT compiled.
throw new LogAsErrorException($"Could not find {fullPath} to AOT");
}
- if (CollectCompiledMethods)
+ if (CollectTrimmingEligibleMethods)
{
- if (string.IsNullOrEmpty(CompiledMethodsOutputDirectory))
- throw new LogAsErrorException($"{nameof(CompiledMethodsOutputDirectory)} is empty. When {nameof(CollectCompiledMethods)} is set to true, the user needs to provide a directory for {nameof(CompiledMethodsOutputDirectory)}.");
+ if (string.IsNullOrEmpty(TrimmingEligibleMethodsOutputDirectory))
+ throw new LogAsErrorException($"{nameof(TrimmingEligibleMethodsOutputDirectory)} is empty. When {nameof(CollectTrimmingEligibleMethods)} is set to true, the user needs to provide a directory for {nameof(TrimmingEligibleMethodsOutputDirectory)}.");
- if (!Directory.Exists(CompiledMethodsOutputDirectory))
+ if (!Directory.Exists(TrimmingEligibleMethodsOutputDirectory))
{
- Directory.CreateDirectory(CompiledMethodsOutputDirectory);
+ Directory.CreateDirectory(TrimmingEligibleMethodsOutputDirectory);
}
}
aotArgs.Add("dedup-skip");
}
- if (CollectCompiledMethods)
+ if (CollectTrimmingEligibleMethods)
{
string assemblyName = assemblyFilename.Replace(".", "_");
string outputFileName = assemblyName + "_compiled_methods.txt";
string outputFilePath;
- if (string.IsNullOrEmpty(CompiledMethodsOutputDirectory))
+ if (string.IsNullOrEmpty(TrimmingEligibleMethodsOutputDirectory))
{
outputFilePath = outputFileName;
}
else
{
- outputFilePath = Path.Combine(CompiledMethodsOutputDirectory, outputFileName);
+ outputFilePath = Path.Combine(TrimmingEligibleMethodsOutputDirectory, outputFileName);
}
- aotArgs.Add($"compiled-methods-outfile={outputFilePath}");
+ aotArgs.Add($"trimming-eligible-methods-outfile={outputFilePath}");
aotAssembly.SetMetadata("MethodTokenFile", outputFilePath);
}
private bool TrimMethods(ITaskItem assemblyItem)
{
+ string assemblyFilePathArg = assemblyItem.ItemSpec;
string methodTokenFile = assemblyItem.GetMetadata("MethodTokenFile");
if (string.IsNullOrEmpty(methodTokenFile))
{
if (isTrimmed)
{
- AddItemToTrimmedList(assemblyFilePath, trimmedAssemblyFilePath);
+ AddItemToTrimmedList(assemblyFilePathArg, trimmedAssemblyFilePath);
}
return true;
int methodSize = ComputeMethodSize(peReader, rva);
int actualLoc = ComputeMethodHash(peReader, rva);
int headerSize = ComputeMethodHeaderSize(memStream, actualLoc);
+ if (headerSize == 1) //Set code size to zero for TinyFormat
+ SetCodeSizeToZeroForTiny(ref memStream, actualLoc);
ZeroOutMethodBody(ref memStream, methodSize, actualLoc, headerSize);
}
else if (count < 0)
return (headerFlag == 2 ? 1 : 4);
}
+ private static void SetCodeSizeToZeroForTiny(ref MemoryStream memStream, int actualLoc)
+ {
+ memStream.Position = actualLoc;
+ byte[] header = {0b10};
+ memStream.Write(header, 0, 1);
+ }
+
private static void ZeroOutMethodBody(ref MemoryStream memStream, int methodSize, int actualLoc, int headerSize)
{
memStream.Position = actualLoc + headerSize;