Skip to content

Commit 6d094a0

Browse files
authored
[mono] Use SROA-friendly struct type declarations (dotnet#59007)
LLVM's SROA can decompose loads and stores of aggregate type into a sequence of aggregate-element-typed loads and stores. Before this change, Mono translated .NET-level value types into LLVM IR-level structs containing nothing but `i8` elements. When a value type field has reference type, and a value of this value type is copied using a `memcpy` intrinsic or an LLVM IR load followed by a store, LLVM will emit code that loads managed references in multiple byte-sized fragments before reconstructing the original pointer using a sequence of ALU ops. This causes sgen to fail to pin the referent. This change works around this by translating value types to LLVM IR structs with pointer-sized fields. Packed value types with non-standard alignment will be translated into LLVM IR structs containing alignment-sized fields. Note that this does not completely guarantee that the code we generate will respect sgen's requirements. No specific guarantees are provided about the translation of non-atomic LLVM IR loads and stores to machine code. And we'll need some alternative means (perhaps a special `gc_copy_unaligned` runtime call or similar) to copy packed or misaligned value types that contain managed references. For stronger LLVM IR-level guarantees, we'll want to make use of unordered atomic loads and stores and unordered atomic memcpy, but that work is out of scope for this change. Fixes dotnet#58062, but see the previous paragraph for caveats. See: - https://github.com/dotnet/llvm-project/blob/release/11.x/llvm/lib/Transforms/Scalar/SROA.cpp#L3371-L3388 - https://github.com/dotnet/llvm-project/blob/release/11.x/llvm/lib/Transforms/Scalar/SROA.cpp#L3327-L3340
1 parent 17221d1 commit 6d094a0

File tree

2 files changed

+25
-18
lines changed

2 files changed

+25
-18
lines changed

src/mono/mono/mini/mini-llvm.c

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -530,23 +530,27 @@ ThisType (void)
530530
return TARGET_SIZEOF_VOID_P == 8 ? LLVMPointerType (LLVMInt64Type (), 0) : LLVMPointerType (LLVMInt32Type (), 0);
531531
}
532532

533+
typedef struct {
534+
int32_t size;
535+
uint32_t align;
536+
} MonoSizeAlign;
537+
533538
/*
534539
* get_vtype_size:
535540
*
536541
* Return the size of the LLVM representation of the vtype T.
537542
*/
538-
static guint32
539-
get_vtype_size (MonoType *t)
543+
static MonoSizeAlign
544+
get_vtype_size_align (MonoType *t)
540545
{
541-
int size;
542-
543-
size = mono_class_value_size (mono_class_from_mono_type_internal (t), NULL);
546+
uint32_t align = 0;
547+
int32_t size = mono_class_value_size (mono_class_from_mono_type_internal (t), &align);
544548

545549
/* LLVMArgAsIArgs depends on this since it stores whole words */
546550
while (size < 2 * TARGET_SIZEOF_VOID_P && mono_is_power_of_two (size) == -1)
547551
size ++;
548-
549-
return size;
552+
MonoSizeAlign ret = { size, align };
553+
return ret;
550554
}
551555

552556
/*
@@ -681,11 +685,17 @@ create_llvm_type_for_type (MonoLLVMModule *module, MonoClass *klass)
681685
for (i = 0; i < size; ++i)
682686
eltypes [i] = esize == 4 ? LLVMFloatType () : LLVMDoubleType ();
683687
} else {
684-
size = get_vtype_size (t);
685-
686-
eltypes = g_new (LLVMTypeRef, size);
687-
for (i = 0; i < size; ++i)
688-
eltypes [i] = LLVMInt8Type ();
688+
MonoSizeAlign size_align = get_vtype_size_align (t);
689+
eltypes = g_new (LLVMTypeRef, size_align.size);
690+
size = 0;
691+
uint32_t bytes = 0;
692+
uint32_t chunk = size_align.align < TARGET_SIZEOF_VOID_P ? size_align.align : TARGET_SIZEOF_VOID_P;
693+
for (; chunk > 0; chunk = chunk >> 1) {
694+
for (; (bytes + chunk) <= size_align.size; bytes += chunk) {
695+
eltypes [size] = LLVMIntType (chunk * 8);
696+
++size;
697+
}
698+
}
689699
}
690700

691701
name = mono_type_full_name (m_class_get_byval_arg (klass));
@@ -2672,11 +2682,11 @@ static void
26722682
emit_vtype_to_args (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMValueRef address, LLVMArgInfo *ainfo, LLVMValueRef *args, guint32 *nargs)
26732683
{
26742684
int pindex = 0;
2675-
int j, size, nslots;
2685+
int j, nslots;
26762686
LLVMTypeRef arg_type;
26772687

26782688
t = mini_get_underlying_type (t);
2679-
size = get_vtype_size (t);
2689+
int32_t size = get_vtype_size_align (t).size;
26802690

26812691
if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type_internal (t)))
26822692
address = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (LLVMInt8Type (), 0), "");
@@ -7342,7 +7352,7 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
73427352
} else if (ainfo->storage == LLVMArgVtypeAddr || values [ins->sreg1] == addresses [ins->sreg1]) {
73437353
/* LLVMArgVtypeByRef/LLVMArgVtypeAddr, have to make a copy */
73447354
addresses [ins->dreg] = build_alloca (ctx, t);
7345-
LLVMValueRef v = LLVMBuildLoad (builder, addresses [ins->sreg1], "");
7355+
LLVMValueRef v = LLVMBuildLoad (builder, addresses [ins->sreg1], "llvm_outarg_vt_copy");
73467356
LLVMBuildStore (builder, convert (ctx, v, type_to_llvm_type (ctx, t)), addresses [ins->dreg]);
73477357
} else {
73487358
addresses [ins->dreg] = addresses [ins->sreg1];

src/tests/issues.targets

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2317,9 +2317,6 @@
23172317
<ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/CLR-x86-JIT/V1.1-M1-Beta1/b143840/b143840/*">
23182318
<Issue>https://github.com/dotnet/runtime/issues/48914</Issue>
23192319
</ExcludeList>
2320-
<ExcludeList Include = "$(XunitTestBinBase)/JIT/Performance/CodeQuality/Roslyn/CscBench/**">
2321-
<Issue>https://github.com/dotnet/runtime/issues/58062</Issue>
2322-
</ExcludeList>
23232320
<ExcludeList Include = "$(XunitTestBinBase)/tracing/eventpipe/eventsourceerror/**">
23242321
<Issue> needs triage </Issue>
23252322
</ExcludeList>

0 commit comments

Comments
 (0)