Skip to content

Commit 23252f8

Browse files
committed
Generated find_pinvoke code appears to be correct
1 parent 56e1096 commit 23252f8

File tree

4 files changed

+203
-53
lines changed

4 files changed

+203
-53
lines changed

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionBody.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ public LlvmIrFunctionBody (LlvmIrFunction func, LlvmIrFunction.FunctionState fun
171171
previousLabel = implicitStartBlock = new LlvmIrFunctionImplicitStartLabel (functionState.StartingBlockNumber);
172172
}
173173

174-
public void Add (LlvmIrFunctionLabelItem label)
174+
public void Add (LlvmIrFunctionLabelItem label, string? comment = null)
175175
{
176176
label.WillAddToBody (this, functionState);
177177
if (definedLabels.Contains (label.Name)) {
@@ -189,13 +189,16 @@ public void Add (LlvmIrFunctionLabelItem label)
189189
precedingBlock1 = previousLabel;
190190
previousLabel = label;
191191

192-
var comment = new StringBuilder (" preds = %");
193-
comment.Append (precedingBlock1.Name);
194-
if (precedingBlock2 != null) {
195-
comment.Append (", %");
196-
comment.Append (precedingBlock2.Name);
192+
if (comment == null) {
193+
var sb = new StringBuilder (" preds = %");
194+
sb.Append (precedingBlock1.Name);
195+
if (precedingBlock2 != null) {
196+
sb.Append (", %");
197+
sb.Append (precedingBlock2.Name);
198+
}
199+
comment = sb.ToString ();
197200
}
198-
label.Comment = comment.ToString ();
201+
label.Comment = comment;
199202
}
200203

201204
public void Add (LlvmIrFunctionBodyItem item)

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrInstructions.cs

Lines changed: 77 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ protected void WriteValue (GeneratorWriteContext context, Type type, object? val
7373
context.Output.Write ("null");
7474
} else if (value is LlvmIrVariable variable) {
7575
context.Output.Write (variable.Reference);
76+
} else if (value is bool) {
77+
context.Output.Write ((bool)value ? "true" : "false");
7678
} else {
7779
context.Output.Write (MonoAndroidHelper.CultureInvariantToString (value));
7880
}
@@ -510,26 +512,48 @@ protected override void WriteBody (GeneratorWriteContext context)
510512

511513
public class Phi : LlvmIrInstruction
512514
{
515+
sealed class Node
516+
{
517+
public readonly LlvmIrVariableReference? Variable;
518+
public readonly LlvmIrFunctionLabelItem? Label;
519+
520+
public Node (LlvmIrVariableReference? variable, LlvmIrFunctionLabelItem? label)
521+
{
522+
Variable = variable;
523+
Label = label;
524+
}
525+
}
526+
513527
LlvmIrVariable result;
514-
LlvmIrVariable val1;
515-
LlvmIrFunctionLabelItem label1;
516-
LlvmIrVariable val2;
517-
LlvmIrFunctionLabelItem label2;
528+
readonly List<Node> nodes;
529+
530+
public Phi (LlvmIrVariable result)
531+
: base ("phi")
532+
{
533+
nodes = new ();
534+
this.result = result;
535+
}
518536

519537
/// <summary>
520538
/// Represents the `phi` instruction form we use the most throughout marshal methods generator - one which refers to an if/else block and where
521539
/// **both** value:label pairs are **required**. Parameters <paramref name="label1"/> and <paramref name="label2"/> are nullable because, in theory,
522540
/// it is possible that <see cref="LlvmIrFunctionBody"/> hasn't had the required blocks defined prior to adding the `phi` instruction and, thus,
523541
/// we must check for the possibility here.
524542
/// </summary>
525-
public Phi (LlvmIrVariable result, LlvmIrVariable val1, LlvmIrFunctionLabelItem? label1, LlvmIrVariable val2, LlvmIrFunctionLabelItem? label2)
543+
public Phi (LlvmIrVariable result, LlvmIrVariableReference val1, LlvmIrFunctionLabelItem? label1, LlvmIrVariableReference val2, LlvmIrFunctionLabelItem? label2)
526544
: base ("phi")
527545
{
528546
this.result = result;
529-
this.val1 = val1;
530-
this.label1 = label1 ?? throw new ArgumentNullException (nameof (label1));
531-
this.val2 = val2;
532-
this.label2 = label2 ?? throw new ArgumentNullException (nameof (label2));
547+
548+
nodes = new () {
549+
new Node (val1, label1 ?? throw new ArgumentNullException (nameof (label1))),
550+
new Node (val2, label2 ?? throw new ArgumentNullException (nameof (label2))),
551+
};
552+
}
553+
554+
public void AddNode (LlvmIrFunctionLabelItem label, LlvmIrVariableReference? variable)
555+
{
556+
nodes.Add (new Node (variable, label));
533557
}
534558

535559
protected override void WriteValueAssignment (GeneratorWriteContext context)
@@ -541,14 +565,42 @@ protected override void WriteValueAssignment (GeneratorWriteContext context)
541565
protected override void WriteBody (GeneratorWriteContext context)
542566
{
543567
context.Output.Write (LlvmIrGenerator.MapToIRType (result.Type, context.TypeCache));
568+
context.IncreaseIndent ();
569+
570+
bool first = true;
571+
foreach (Node node in nodes) {
572+
if (!first) {
573+
context.Output.WriteLine (',');
574+
} else {
575+
first = false;
576+
context.Output.WriteLine ();
577+
}
578+
context.Output.Write (context.CurrentIndent);
579+
WriteNode (context, node);
580+
}
581+
context.DecreaseIndent ();
582+
583+
// context.Output.Write (" [");
584+
// context.Output.Write (val1.Reference);
585+
// context.Output.Write (", %");
586+
// context.Output.Write (label1.Name);
587+
// context.Output.Write ("], [");
588+
// context.Output.Write (val2.Reference);
589+
// context.Output.Write (", %");
590+
// context.Output.Write (label2.Name);
591+
// context.Output.Write (']');
592+
}
593+
594+
void WriteNode (GeneratorWriteContext context, Node node)
595+
{
596+
if (node.Label == null) {
597+
throw new NotImplementedException ("Internal error: null labels not implemented");
598+
}
599+
544600
context.Output.Write (" [");
545-
context.Output.Write (val1.Reference);
546-
context.Output.Write (", %");
547-
context.Output.Write (label1.Name);
548-
context.Output.Write ("], [");
549-
context.Output.Write (val2.Reference);
601+
context.Output.Write (node.Variable == null ? "null" : node.Variable.Reference);
550602
context.Output.Write (", %");
551-
context.Output.Write (label2.Name);
603+
context.Output.Write (node.Label.Name);
552604
context.Output.Write (']');
553605
}
554606
}
@@ -594,6 +646,13 @@ public Store (LlvmIrVariable from, LlvmIrVariable to)
594646
this.to = to;
595647
}
596648

649+
public Store (object from, LlvmIrVariable to)
650+
: base (Opcode)
651+
{
652+
this.from = from;
653+
this.to = to;
654+
}
655+
597656
/// <summary>
598657
/// Stores `null` in the indicated variable
599658
/// </summary>
@@ -605,7 +664,8 @@ public Store (LlvmIrVariable to)
605664

606665
protected override void WriteBody (GeneratorWriteContext context)
607666
{
608-
string irType = LlvmIrGenerator.MapToIRType (to.Type, context.TypeCache, out ulong size, out bool isPointer);
667+
Type type = from?.GetType () ?? to.Type;
668+
string irType = LlvmIrGenerator.MapToIRType (type, context.TypeCache, out ulong size, out bool isPointer);
609669
context.Output.Write (irType);
610670
context.Output.Write (' ');
611671

@@ -687,6 +747,7 @@ protected override void WriteBody (GeneratorWriteContext context)
687747
context.Output.Write (", label %");
688748
context.Output.Write (label.Name);
689749
if (!String.IsNullOrEmpty (comment)) {
750+
context.Output.Write (' ');
690751
context.Generator.WriteCommentLine (context, comment);
691752
} else {
692753
context.Output.WriteLine ();

src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrVariable.cs

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,45 @@ enum LlvmIrVariableNumberFormat
2121
Decimal,
2222
}
2323

24-
abstract class LlvmIrVariable : IEquatable<LlvmIrVariable>
24+
abstract class LlvmIrVariableReference
2525
{
2626
public abstract bool Global { get; }
2727
public abstract string NamePrefix { get; }
28-
2928
public string? Name { get; protected set; }
29+
30+
/// <summary>
31+
/// Returns a string which constitutes a reference to a local (using the <c>%</c> prefix character) or a global
32+
/// (using the <c>@</c> prefix character) variable, ready for use in the generated code wherever variables are
33+
/// referenced.
34+
/// </summary>
35+
public virtual string Reference {
36+
get {
37+
if (String.IsNullOrEmpty (Name)) {
38+
throw new InvalidOperationException ("Variable doesn't have a name, it cannot be referenced");
39+
}
40+
41+
return $"{NamePrefix}{Name}";
42+
}
43+
}
44+
45+
protected LlvmIrVariableReference (string name)
46+
{
47+
Name = name;
48+
}
49+
}
50+
51+
class LlvmIrGlobalVariableReference : LlvmIrVariableReference
52+
{
53+
public override bool Global => true;
54+
public override string NamePrefix => "@";
55+
56+
public LlvmIrGlobalVariableReference (string name)
57+
: base (name)
58+
{}
59+
}
60+
61+
abstract class LlvmIrVariable : LlvmIrVariableReference, IEquatable<LlvmIrVariable>
62+
{
3063
public Type Type { get; protected set; }
3164
public LlvmIrVariableWriteOptions WriteOptions { get; set; } = LlvmIrVariableWriteOptions.ArrayWriteIndexComments;
3265

@@ -47,21 +80,6 @@ abstract class LlvmIrVariable : IEquatable<LlvmIrVariable>
4780
/// will ignore name when checking for equality.
4881
protected bool NameMatters { get; set; } = true;
4982

50-
/// <summary>
51-
/// Returns a string which constitutes a reference to a local (using the <c>%</c> prefix character) or a global
52-
/// (using the <c>@</c> prefix character) variable, ready for use in the generated code wherever variables are
53-
/// referenced.
54-
/// </summary>
55-
public virtual string Reference {
56-
get {
57-
if (String.IsNullOrEmpty (Name)) {
58-
throw new InvalidOperationException ("Variable doesn't have a name, it cannot be referenced");
59-
}
60-
61-
return $"{NamePrefix}{Name}";
62-
}
63-
}
64-
6583
/// <summary>
6684
/// <para>
6785
/// Certain data must be calculated when the target architecture is known, because it may depend on certain aspects of
@@ -103,6 +121,7 @@ public virtual string Reference {
103121
/// is treated as an opaque pointer type.
104122
/// </summary>
105123
protected LlvmIrVariable (Type type, string? name = null)
124+
: base (name)
106125
{
107126
Type = type;
108127
Name = name;
@@ -253,7 +272,7 @@ class LlvmIrGlobalVariable : LlvmIrVariable
253272
public LlvmIrStreamedArrayDataProvider? ArrayDataProvider { get; set; }
254273

255274
/// <summary>
256-
/// Constructs a local variable. <paramref name="type"/> is translated to one of the LLVM IR first class types (see
275+
/// Constructs a global variable. <paramref name="type"/> is translated to one of the LLVM IR first class types (see
257276
/// https://llvm.org/docs/LangRef.html#t-firstclass) only if it's an integral or floating point type. In all other cases it
258277
/// is treated as an opaque pointer type. <paramref name="name"/> is required because global variables must be named.
259278
/// </summary>

src/Xamarin.Android.Build.Tasks/Utilities/PreservePinvokesNativeAssemblyGenerator.cs

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ public void Sort ()
5858
}
5959
}
6060

61+
sealed class ConstructionState
62+
{
63+
public LlvmIrFunction Func;
64+
public LlvmIrFunctionLabelItem ReturnLabel;
65+
public LlvmIrFunctionParameter EntryPointHashParam;
66+
public LlvmIrInstructions.Phi Phi;
67+
public bool Is64Bit;
68+
}
69+
6170
// Maps a component name after ridding it of the `lib` prefix and the extension to a "canonical"
6271
// name of a library, as used in `[DllImport]` attributes.
6372
readonly Dictionary<string, string> libraryNameMap = new (StringComparer.Ordinal) {
@@ -181,36 +190,94 @@ void AddFindPinvoke<T> (LlvmIrModule module, List<Component> components, bool is
181190
}
182191
);
183192

184-
// TODO: attributes
185193
var func = new LlvmIrFunction (sig, MakeFindPinvokeAttributeSet (module)) {
186194
CallingConvention = LlvmIrCallingConvention.Fastcc,
187195
Linkage = LlvmIrLinkage.Internal,
188196
};
189-
module.Add (func);
190-
func.Body.Add (new LlvmIrFunctionLabelItem ("entry"));
197+
LlvmIrLocalVariable retval = func.CreateLocalVariable (typeof(IntPtr), "retval");
198+
var state = new ConstructionState {
199+
Func = func,
200+
ReturnLabel = new LlvmIrFunctionLabelItem ("return"),
201+
EntryPointHashParam = parameters[1],
202+
Phi = new LlvmIrInstructions.Phi (retval),
203+
Is64Bit = is64Bit,
204+
};
205+
module.Add (state.Func);
206+
state.Func.Body.Add (new LlvmIrFunctionLabelItem ("entry"));
191207

192208
var libraryNameSwitchEpilog = new LlvmIrFunctionLabelItem ("libNameSW.epilog");
193209
var componentSwitch = new LlvmIrInstructions.Switch<T> (parameters[0], libraryNameSwitchEpilog, "sw.libname");
194-
func.Body.Add (componentSwitch);
210+
211+
state.Func.Body.Add (componentSwitch);
212+
state.Phi.AddNode (libraryNameSwitchEpilog, null);
195213

196214
components.Sort ((Component a, Component b) => a.NameHash.CompareTo (b.NameHash));
197215
Log.LogDebugMessage (" Components to be preserved:");
216+
uint componentID = 1;
217+
198218
foreach (Component component in components) {
199-
component.Sort ();
200219
Log.LogDebugMessage ($" {component.Name} (hash: 0x{component.NameHash:x}; {component.PInvokes.Count} p/invoke(s))");
201220

202-
LlvmIrFunctionLabelItem componentLabel;
203-
if (is64Bit) {
204-
componentLabel = componentSwitch.Add ((T)(object)component.NameHash);
221+
string comment = $" {component.Name}";
222+
LlvmIrFunctionLabelItem componentLabel = AddSwitchItem<T> (componentSwitch, component.NameHash, is64Bit, comment, null);
223+
224+
func.Body.Add (componentLabel, comment);
225+
AddPInvokeSwitch<T> (state, componentLabel, component, componentID++);
226+
}
227+
228+
func.Body.Add (libraryNameSwitchEpilog);
229+
230+
var setKnownLib = new LlvmIrInstructions.Store (false, parameters[2]);
231+
func.Body.Add (setKnownLib);
232+
AddReturnBranch (func, state.ReturnLabel);
233+
234+
func.Body.Add (state.ReturnLabel);
235+
func.Body.Add (state.Phi);
236+
func.Body.Add (new LlvmIrInstructions.Ret (typeof (IntPtr), retval));
237+
}
238+
239+
void AddPInvokeSwitch<T> (ConstructionState state, LlvmIrFunctionLabelItem componentLabel, Component component, uint id) where T: struct
240+
{
241+
var pinvokeSwitchEpilog = new LlvmIrFunctionLabelItem ($"pinvokeSW.epilog.{id}");
242+
state.Phi.AddNode (pinvokeSwitchEpilog, null);
243+
244+
var pinvokeSwitch = new LlvmIrInstructions.Switch<T> (state.EntryPointHashParam, pinvokeSwitchEpilog, $"sw.pinvoke.{id}");
245+
state.Func.Body.Add (pinvokeSwitch);
246+
247+
component.Sort ();
248+
bool first = true;
249+
foreach (PInvoke pi in component.PInvokes) {
250+
string pinvokeName = pi.NativeFunction.Signature.Name;
251+
string comment = $" {pinvokeName}";
252+
LlvmIrFunctionLabelItem pinvokeLabel = AddSwitchItem<T> (pinvokeSwitch, pi.Hash, state.Is64Bit, comment, first ? state.ReturnLabel : null);
253+
254+
// First item of every component switch block "reuses" the block's label
255+
if (first) {
256+
first = false;
205257
} else {
206-
componentLabel = componentSwitch.Add ((T)(object)(uint)component.NameHash);
258+
state.Func.Body.Add (pinvokeLabel, comment);
259+
AddReturnBranch (state.Func, state.ReturnLabel);
207260
}
208261

209-
func.Body.Add (componentLabel);
210-
// TODO: output component `switch` here
262+
state.Phi.AddNode (pinvokeLabel == state.ReturnLabel ? componentLabel : pinvokeLabel, new LlvmIrGlobalVariableReference (pinvokeName));
211263
}
212264

213-
func.Body.Add (libraryNameSwitchEpilog);
265+
state.Func.Body.Add (pinvokeSwitchEpilog);
266+
AddReturnBranch (state.Func, state.ReturnLabel);
267+
}
268+
269+
void AddReturnBranch (LlvmIrFunction func, LlvmIrFunctionLabelItem returnLabel)
270+
{
271+
var branch = new LlvmIrInstructions.Br (returnLabel);
272+
func.Body.Add (branch);
273+
}
274+
275+
LlvmIrFunctionLabelItem AddSwitchItem<T> (LlvmIrInstructions.Switch<T> sw, ulong hash, bool is64Bit, string? comment, LlvmIrFunctionLabelItem? label) where T: struct
276+
{
277+
if (is64Bit) {
278+
return sw.Add ((T)(object)hash, dest: label, comment: comment);
279+
}
280+
return sw.Add ((T)(object)(uint)hash, dest: label, comment: comment);
214281
}
215282

216283
LlvmIrFunctionAttributeSet MakeFindPinvokeAttributeSet (LlvmIrModule module)

0 commit comments

Comments
 (0)