Skip to content

Commit 4bafed5

Browse files
authored
Simplification of array parameters for interop calls (#255)
- To say that the AOT source generator marshaling was under documented would be the understatement of the century. Sadly, that includes marshaling of arrays, especially arrays of safe handles (or in this case global Handles) This PR contains interop wrapper function that take care of the heavy lifting as needed. Co-authored-by: smaillet <25911635+smaillet@users.noreply.github.com>
1 parent 0d9f25a commit 4bafed5

File tree

10 files changed

+275
-56
lines changed

10 files changed

+275
-56
lines changed

src/Interop/InteropTests/DebugRecordTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public void DebugRecordEnumerationSucceeds( )
7373

7474
LLVMMetadataRef int32PtrDiType = LLVMDIBuilderCreatePointerType(diBuilder, int32DiType, 32, 0, 0, "int*"u8);
7575
LLVMMetadataRef emptyExpression = LLVMDIBuilderCreateExpression(diBuilder, []);
76-
LLVMMetadataRef diFuncType = LLVMDIBuilderCreateSubroutineType(diBuilder, default, [], 0, LLVMDIFlags.LLVMDIFlagPrivate);
76+
LLVMMetadataRef diFuncType = LLVMDIBuilderCreateSubroutineType(diBuilder, default, [], LLVMDIFlags.LLVMDIFlagPrivate);
7777
LLVMMetadataRef scope = LLVMDIBuilderCreateFunction(
7878
diBuilder,
7979
Scope: default,

src/Interop/Ubiquity.NET.Llvm.Interop/ABI/llvm-c/Core.cs

Lines changed: 99 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,6 @@ public static void LLVMSetSourceFileName( LLVMModuleRefAlias M, LazyEncodedStrin
710710
// so the types are problematic in full verification. In reality, len will NOT exceed the uint.MaxValue
711711
[LibraryImport( LibraryName )]
712712
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
713-
[return: MarshalUsing( CountElementName = nameof( Len ) )]
714713
public static unsafe partial LLVMModuleFlagEntry LLVMCopyModuleFlagsMetadata( LLVMModuleRefAlias M, out nuint Len );
715714

716715
[LibraryImport( LibraryName )]
@@ -3055,12 +3054,32 @@ uint NumArgs
30553054
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
30563055
public static unsafe partial uint LLVMGetCallSiteAttributeCount( LLVMValueRef C, LLVMAttributeIndex Idx );
30573056

3057+
public static LLVMAttributeRef[] LLVMGetCallSiteAttributes(LLVMValueRef c, LLVMAttributeIndex index)
3058+
{
3059+
uint count = LLVMGetCallSiteAttributeCount(c, index );
3060+
if(count == 0)
3061+
{
3062+
return [];
3063+
}
3064+
3065+
var retVal = new LLVMAttributeRef[ count ];
3066+
unsafe
3067+
{
3068+
fixed(LLVMAttributeRef* pBuf = retVal)
3069+
{
3070+
LLVMGetCallSiteAttributes( c, index, pBuf );
3071+
}
3072+
}
3073+
3074+
return retVal;
3075+
}
3076+
30583077
// size of Attrs must contain enough room for LLVMGetCallSiteAttributeCount() elements or memory corruption
30593078
// will occur. The native code only deals with a pointer and assumes it points to a region big enough to
30603079
// hold the correct amount.
30613080
[LibraryImport( LibraryName )]
30623081
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
3063-
public static unsafe partial void LLVMGetCallSiteAttributes(
3082+
private static unsafe partial void LLVMGetCallSiteAttributes(
30643083
LLVMValueRef C,
30653084
LLVMAttributeIndex Idx,
30663085
/*LLVMAttributeRef[LLVMGetCallSiteAttributeCount(C, Idx)]*/LLVMAttributeRef* Attrs
@@ -3401,6 +3420,7 @@ private static unsafe partial LLVMValueRef LLVMBuildCallBr(
34013420
LazyEncodedString Name
34023421
);
34033422

3423+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
34043424
public static LLVMValueRef LLVMBuildInvoke2(
34053425
LLVMBuilderRef builder,
34063426
LLVMTypeRef typeRef,
@@ -3434,9 +3454,40 @@ private static unsafe partial LLVMValueRef LLVMBuildInvoke2(
34343454
LLVMBasicBlockRef Catch,
34353455
LazyEncodedString Name );
34363456

3457+
public static LLVMValueRef LLVMBuildInvokeWithOperandBundles(
3458+
LLVMBuilderRef builder,
3459+
LLVMTypeRef typeRef,
3460+
LLVMValueRef functionValue,
3461+
LLVMValueRef[] args,
3462+
LLVMBasicBlockRef @then,
3463+
LLVMBasicBlockRef @catch,
3464+
LLVMOperandBundleRef[] bundles,
3465+
LazyEncodedString name
3466+
)
3467+
{
3468+
unsafe
3469+
{
3470+
return RefHandleMarshaller.WithNativePointer(
3471+
bundles,
3472+
( p, size ) => LLVMBuildInvokeWithOperandBundles(
3473+
builder,
3474+
typeRef,
3475+
functionValue,
3476+
args,
3477+
checked((uint)args.Length),
3478+
@then,
3479+
@catch,
3480+
p,
3481+
checked((uint)size),
3482+
name
3483+
)
3484+
);
3485+
}
3486+
}
3487+
34373488
[LibraryImport( LibraryName )]
34383489
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
3439-
public static unsafe partial LLVMValueRef LLVMBuildInvokeWithOperandBundles(
3490+
private static unsafe partial LLVMValueRef LLVMBuildInvokeWithOperandBundles(
34403491
LLVMBuilderRef B,
34413492
LLVMTypeRef Ty,
34423493
LLVMValueRef Fn,
@@ -3480,6 +3531,7 @@ LazyEncodedString Name
34803531
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
34813532
public static unsafe partial LLVMValueRef LLVMBuildCatchRet( LLVMBuilderRef B, LLVMValueRef CatchPad, LLVMBasicBlockRef BB );
34823533

3534+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
34833535
public static LLVMValueRef LLVMBuildCatchPad(
34843536
LLVMBuilderRef B,
34853537
LLVMValueRef ParentPad,
@@ -3503,14 +3555,23 @@ private static unsafe partial LLVMValueRef LLVMBuildCatchPad(
35033555
LazyEncodedString Name
35043556
);
35053557

3558+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3559+
public static LLVMValueRef LLVMBuildCleanupPad(
3560+
LLVMBuilderRef B,
3561+
LLVMValueRef ParentPad,
3562+
LLVMValueRef[] Args,
3563+
LazyEncodedString Name
3564+
)
3565+
{
3566+
return LLVMBuildCleanupPad(B, ParentPad, Args, checked((uint)Args.Length), Name);
3567+
}
3568+
35063569
[LibraryImport( LibraryName )]
35073570
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
3508-
public static unsafe partial LLVMValueRef LLVMBuildCleanupPad(
3571+
private static unsafe partial LLVMValueRef LLVMBuildCleanupPad(
35093572
LLVMBuilderRef B,
35103573
LLVMValueRef ParentPad,
3511-
3512-
// No marshalling attribute exists to set the length, the length of this array MUST be at least NumArgs
3513-
[In] LLVMValueRef[] Args,
3574+
[In] LLVMValueRef[] Args, // the length of this array MUST be at least NumArgs
35143575
uint NumArgs,
35153576
LazyEncodedString Name
35163577
);
@@ -3587,7 +3648,7 @@ public static LLVMBasicBlockRef[] LLVMGetHandlers( LLVMValueRef CatchSwitch )
35873648
// Array provided for 'Handlers' must be at least (LLVMGetNumHandlers()) large
35883649
// Use of generated marshalling is VERY inefficient as it doesn't know
35893650
// that a LLVMBasicBlockRef is reinterpret_cast<nint> compatible.
3590-
// and tries to use a buffer array to `marshal` between forms even though
3651+
// and tries to use a retVal array to `marshal` between forms even though
35913652
// they have the exact same bit pattern
35923653
[LibraryImport( LibraryName )]
35933654
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
@@ -4006,9 +4067,36 @@ LazyEncodedString Name
40064067
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
40074068
public static unsafe partial LLVMValueRef LLVMBuildCall2( LLVMBuilderRef B, LLVMTypeRef _1, LLVMValueRef Fn, [In] LLVMValueRef[] Args, uint NumArgs, LazyEncodedString Name );
40084069

4070+
public static LLVMValueRef LLVMBuildCallWithOperandBundles(
4071+
LLVMBuilderRef B,
4072+
LLVMTypeRef _1,
4073+
LLVMValueRef Fn,
4074+
LLVMValueRef[] Args,
4075+
LLVMOperandBundleRef[] Bundles,
4076+
LazyEncodedString Name
4077+
)
4078+
{
4079+
unsafe
4080+
{
4081+
return RefHandleMarshaller.WithNativePointer(
4082+
Bundles,
4083+
( p, size ) => LLVMBuildCallWithOperandBundles(
4084+
B,
4085+
_1,
4086+
Fn,
4087+
Args,
4088+
checked((uint)Args.Length),
4089+
p,
4090+
checked((uint)size),
4091+
Name
4092+
)
4093+
);
4094+
}
4095+
}
4096+
40094097
[LibraryImport( LibraryName )]
40104098
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
4011-
public static unsafe partial LLVMValueRef LLVMBuildCallWithOperandBundles(
4099+
private static unsafe partial LLVMValueRef LLVMBuildCallWithOperandBundles(
40124100
LLVMBuilderRef B,
40134101
LLVMTypeRef _1,
40144102
LLVMValueRef Fn,
@@ -4234,8 +4322,8 @@ public static unsafe partial LLVMStatus LLVMCreateMemoryBufferWithContentsOfFile
42344322
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
42354323
public static unsafe partial LLVMStatus LLVMCreateMemoryBufferWithSTDIN( out LLVMMemoryBufferRef OutMemBuf, out string OutMessage );
42364324

4237-
// NOTE: This does NOT use an array as a param as it MUST remain valid and fixed for the lifetime of this
4238-
// buffer ref. That is it builds a reference to the data - NOT a copy.
4325+
// NOTE: This does NOT use an array or span as a param as the data MUST remain valid ***and*** fixed for the lifetime of this
4326+
// retVal ref. That is this builds a MemoryBuffer with a reference to the data - NOT a copy of it!
42394327
[LibraryImport( LibraryName )]
42404328
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
42414329
public static unsafe partial LLVMMemoryBufferRef LLVMCreateMemoryBufferWithMemoryRange(

src/Interop/Ubiquity.NET.Llvm.Interop/ABI/llvm-c/DebugInfo.cs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -414,9 +414,22 @@ public static unsafe partial LLVMMetadataRef LLVMDIBuilderCreateImportedModuleFr
414414
uint Line
415415
);
416416

417+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
418+
public static LLVMMetadataRef LLVMDIBuilderCreateImportedModuleFromAlias(
419+
LLVMDIBuilderRef Builder,
420+
LLVMMetadataRef Scope,
421+
LLVMMetadataRef ImportedEntity,
422+
LLVMMetadataRef File,
423+
uint Line,
424+
LLVMMetadataRef[] Elements
425+
)
426+
{
427+
return LLVMDIBuilderCreateImportedModuleFromAlias(Builder, Scope, ImportedEntity, File, Line, Elements, checked((uint)Elements.Length));
428+
}
429+
417430
[LibraryImport( LibraryName )]
418431
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
419-
public static unsafe partial LLVMMetadataRef LLVMDIBuilderCreateImportedModuleFromAlias(
432+
private static unsafe partial LLVMMetadataRef LLVMDIBuilderCreateImportedModuleFromAlias(
420433
LLVMDIBuilderRef Builder,
421434
LLVMMetadataRef Scope,
422435
LLVMMetadataRef ImportedEntity,
@@ -426,9 +439,22 @@ public static unsafe partial LLVMMetadataRef LLVMDIBuilderCreateImportedModuleFr
426439
uint NumElements // NumElements MUST be <= Elements.Length
427440
);
428441

442+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
443+
public static LLVMMetadataRef LLVMDIBuilderCreateImportedModuleFromModule(
444+
LLVMDIBuilderRef Builder,
445+
LLVMMetadataRef Scope,
446+
LLVMMetadataRef M,
447+
LLVMMetadataRef File,
448+
uint Line,
449+
LLVMMetadataRef[] Elements
450+
)
451+
{
452+
return LLVMDIBuilderCreateImportedModuleFromModule(Builder, Scope, M, File, Line, Elements, checked((uint)Elements.Length));
453+
}
454+
429455
[LibraryImport( LibraryName )]
430456
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
431-
public static unsafe partial LLVMMetadataRef LLVMDIBuilderCreateImportedModuleFromModule(
457+
private static unsafe partial LLVMMetadataRef LLVMDIBuilderCreateImportedModuleFromModule(
432458
LLVMDIBuilderRef Builder,
433459
LLVMMetadataRef Scope,
434460
LLVMMetadataRef M,
@@ -559,9 +585,20 @@ private static unsafe partial LLVMMetadataRef LLVMDIBuilderGetOrCreateTypeArray(
559585
nuint NumElements // NumElements MUST be <= Data.Length
560586
);
561587

588+
[MethodImpl( MethodImplOptions.AggressiveInlining )]
589+
public static LLVMMetadataRef LLVMDIBuilderCreateSubroutineType(
590+
LLVMDIBuilderRef Builder,
591+
LLVMMetadataRef File,
592+
LLVMMetadataRef[] ParameterTypes,
593+
LLVMDIFlags Flags
594+
)
595+
{
596+
return LLVMDIBuilderCreateSubroutineType(Builder, File, ParameterTypes, checked((uint)ParameterTypes.Length), Flags);
597+
}
598+
562599
[LibraryImport( LibraryName )]
563600
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
564-
public static unsafe partial LLVMMetadataRef LLVMDIBuilderCreateSubroutineType(
601+
private static unsafe partial LLVMMetadataRef LLVMDIBuilderCreateSubroutineType(
565602
LLVMDIBuilderRef Builder,
566603
LLVMMetadataRef File,
567604
[In] LLVMMetadataRef[] ParameterTypes,
@@ -602,7 +639,12 @@ private static unsafe partial LLVMMetadataRef LLVMDIBuilderCreateMacro(
602639

603640
[LibraryImport( LibraryName )]
604641
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
605-
public static unsafe partial LLVMMetadataRef LLVMDIBuilderCreateTempMacroFile( LLVMDIBuilderRef Builder, LLVMMetadataRef ParentMacroFile, uint Line, LLVMMetadataRef File );
642+
public static unsafe partial LLVMMetadataRef LLVMDIBuilderCreateTempMacroFile(
643+
LLVMDIBuilderRef Builder,
644+
LLVMMetadataRef ParentMacroFile,
645+
uint Line,
646+
LLVMMetadataRef File
647+
);
606648

607649
[MethodImpl( MethodImplOptions.AggressiveInlining )]
608650
public static LLVMMetadataRef LLVMDIBuilderCreateEnumerator(
@@ -1248,7 +1290,7 @@ public static LLVMMetadataRef LLVMDIBuilderCreateClassType(
12481290
UInt64 OffsetInBits,
12491291
LLVMDIFlags Flags,
12501292
LLVMMetadataRef DerivedFrom,
1251-
[In] LLVMMetadataRef[] Elements,
1293+
LLVMMetadataRef[] Elements,
12521294
LLVMMetadataRef VTableHolder,
12531295
LLVMMetadataRef TemplateParamsNode,
12541296
LazyEncodedString UniqueIdentifier

src/Interop/Ubiquity.NET.Llvm.Interop/ABI/llvm-c/Disassembler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,15 @@ LLVMSymbolLookupCallback SymbolLookUp
125125
[return: MarshalAs( UnmanagedType.Bool )]
126126
public static unsafe partial bool LLVMSetDisasmOptions( LLVMDisasmContextRef DC, UInt64 Options );
127127

128+
// OutString is a pre-allocated buffer, OutStringSize is [In] param to indicate how big it is. (Text is Truncated if not big enough)
128129
[LibraryImport( LibraryName )]
129130
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
130131
public static unsafe partial nuint LLVMDisasmInstruction(
131132
LLVMDisasmContextRef DC,
132133
byte* Bytes,
133134
UInt64 BytesSize,
134135
UInt64 PC,
135-
byte* OutString, nuint OutStringSize // OutString is a pre-allocated buffer, size is how big it is. (Truncated if not big enough)
136+
byte* OutString, nuint OutStringSize
136137
);
137138
}
138139
}

src/Interop/Ubiquity.NET.Llvm.Interop/ABI/llvm-c/DisassemblerTypes.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
namespace Ubiquity.NET.Llvm.Interop
88
{
9+
// NOTE: these are intentionally, NOT record structs as those do NOT allow unsafe types as fields (CS8908)
10+
// Sadly, there is a bug where that error won't show in an IDE so it might look safe/OK until you build
11+
// (see: https://github.com/dotnet/roslyn/issues/58878)
12+
913
[StructLayout( LayoutKind.Sequential )]
1014
public unsafe readonly struct LLVMOpInfoSymbol1
1115
: IEquatable<LLVMOpInfoSymbol1>

0 commit comments

Comments
 (0)