Skip to content

Commit

Permalink
[mono] Use SROA-friendly struct type declarations (#59007)
Browse files Browse the repository at this point in the history
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 #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
  • Loading branch information
imhameed authored Sep 14, 2021
1 parent 17221d1 commit 6d094a0
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 18 deletions.
40 changes: 25 additions & 15 deletions src/mono/mono/mini/mini-llvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -530,23 +530,27 @@ ThisType (void)
return TARGET_SIZEOF_VOID_P == 8 ? LLVMPointerType (LLVMInt64Type (), 0) : LLVMPointerType (LLVMInt32Type (), 0);
}

typedef struct {
int32_t size;
uint32_t align;
} MonoSizeAlign;

/*
* get_vtype_size:
*
* Return the size of the LLVM representation of the vtype T.
*/
static guint32
get_vtype_size (MonoType *t)
static MonoSizeAlign
get_vtype_size_align (MonoType *t)
{
int size;

size = mono_class_value_size (mono_class_from_mono_type_internal (t), NULL);
uint32_t align = 0;
int32_t size = mono_class_value_size (mono_class_from_mono_type_internal (t), &align);

/* LLVMArgAsIArgs depends on this since it stores whole words */
while (size < 2 * TARGET_SIZEOF_VOID_P && mono_is_power_of_two (size) == -1)
size ++;

return size;
MonoSizeAlign ret = { size, align };
return ret;
}

/*
Expand Down Expand Up @@ -681,11 +685,17 @@ create_llvm_type_for_type (MonoLLVMModule *module, MonoClass *klass)
for (i = 0; i < size; ++i)
eltypes [i] = esize == 4 ? LLVMFloatType () : LLVMDoubleType ();
} else {
size = get_vtype_size (t);

eltypes = g_new (LLVMTypeRef, size);
for (i = 0; i < size; ++i)
eltypes [i] = LLVMInt8Type ();
MonoSizeAlign size_align = get_vtype_size_align (t);
eltypes = g_new (LLVMTypeRef, size_align.size);
size = 0;
uint32_t bytes = 0;
uint32_t chunk = size_align.align < TARGET_SIZEOF_VOID_P ? size_align.align : TARGET_SIZEOF_VOID_P;
for (; chunk > 0; chunk = chunk >> 1) {
for (; (bytes + chunk) <= size_align.size; bytes += chunk) {
eltypes [size] = LLVMIntType (chunk * 8);
++size;
}
}
}

name = mono_type_full_name (m_class_get_byval_arg (klass));
Expand Down Expand Up @@ -2672,11 +2682,11 @@ static void
emit_vtype_to_args (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMValueRef address, LLVMArgInfo *ainfo, LLVMValueRef *args, guint32 *nargs)
{
int pindex = 0;
int j, size, nslots;
int j, nslots;
LLVMTypeRef arg_type;

t = mini_get_underlying_type (t);
size = get_vtype_size (t);
int32_t size = get_vtype_size_align (t).size;

if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type_internal (t)))
address = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (LLVMInt8Type (), 0), "");
Expand Down Expand Up @@ -7342,7 +7352,7 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
} else if (ainfo->storage == LLVMArgVtypeAddr || values [ins->sreg1] == addresses [ins->sreg1]) {
/* LLVMArgVtypeByRef/LLVMArgVtypeAddr, have to make a copy */
addresses [ins->dreg] = build_alloca (ctx, t);
LLVMValueRef v = LLVMBuildLoad (builder, addresses [ins->sreg1], "");
LLVMValueRef v = LLVMBuildLoad (builder, addresses [ins->sreg1], "llvm_outarg_vt_copy");
LLVMBuildStore (builder, convert (ctx, v, type_to_llvm_type (ctx, t)), addresses [ins->dreg]);
} else {
addresses [ins->dreg] = addresses [ins->sreg1];
Expand Down
3 changes: 0 additions & 3 deletions src/tests/issues.targets
Original file line number Diff line number Diff line change
Expand Up @@ -2317,9 +2317,6 @@
<ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/CLR-x86-JIT/V1.1-M1-Beta1/b143840/b143840/*">
<Issue>https://github.com/dotnet/runtime/issues/48914</Issue>
</ExcludeList>
<ExcludeList Include = "$(XunitTestBinBase)/JIT/Performance/CodeQuality/Roslyn/CscBench/**">
<Issue>https://github.com/dotnet/runtime/issues/58062</Issue>
</ExcludeList>
<ExcludeList Include = "$(XunitTestBinBase)/tracing/eventpipe/eventsourceerror/**">
<Issue> needs triage </Issue>
</ExcludeList>
Expand Down

0 comments on commit 6d094a0

Please sign in to comment.