Skip to content

Commit 56e1096

Browse files
committed
Code generation progress
1 parent 105d44a commit 56e1096

File tree

6 files changed

+282
-18
lines changed

6 files changed

+282
-18
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Xamarin.Android.Tasks.LLVMIR;
5+
6+
/// <summary>
7+
/// Function calling convention, see https://llvm.org/docs/LangRef.html#callingconv for more detailed docs.
8+
/// Not all conventions are included in this enumeration, only those we may potentially need.
9+
/// </summary>
10+
enum LlvmIrCallingConvention
11+
{
12+
/// <summary>
13+
/// Outputs no keyword, making function use whatever is the compiler's default calling convention.
14+
/// </summary>
15+
Default,
16+
17+
/// <summary>
18+
/// The C calling convention (`ccc`)
19+
/// </summary>
20+
Ccc,
21+
22+
/// <summary>
23+
/// The fast calling convention (`fastcc`). This calling convention attempts to make calls as fast
24+
/// as possible (e.g. by passing things in registers).
25+
/// </summary>
26+
Fastcc,
27+
28+
/// <summary>
29+
/// Tail callable calling convention (`tailcc`). This calling convention ensures that calls in tail
30+
/// position will always be tail call optimized. This calling convention is equivalent to fastcc,
31+
/// except for an additional guarantee that tail calls will be produced whenever possible.
32+
/// </summary>
33+
Tailcc,
34+
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ sealed class SavedParameterState : ILlvmIrSavedFunctionParameterState
2121
public bool? ReadNone;
2222
public bool? SignExt;
2323
public bool? ZeroExt;
24+
public bool? WriteOnly;
2425
public bool? IsCplusPlusReference;
2526
public bool IsVarArgs;
2627

@@ -40,6 +41,7 @@ public SavedParameterState (LlvmIrFunctionParameter owner, SavedParameterState?
4041
ReadNone = previousState.ReadNone;
4142
SignExt = previousState.SignExt;
4243
ZeroExt = previousState.ZeroExt;
44+
WriteOnly = previousState.WriteOnly;
4345
IsCplusPlusReference = previousState.IsCplusPlusReference;
4446
IsVarArgs = previousState.IsVarArgs;
4547
}
@@ -131,6 +133,14 @@ public bool? ZeroExt {
131133
set => state.ZeroExt = value;
132134
}
133135

136+
/// <summary>
137+
/// <c>writeonly</c> attribute, see <see href="https://github.com/llvm/llvm-project/blob/5729e63ac7b47c6ad40f904fedafad3c07cf71ea/llvm/docs/LangRef.rst#L1436"/>
138+
/// </summary>
139+
public bool? WriteOnly {
140+
get => state.WriteOnly;
141+
set => state.WriteOnly = value;
142+
}
143+
134144
/// <summary>
135145
/// This serves a purely documentational purpose, when generating comments about types. It describes a parameter that is a C++ reference, something we can't
136146
/// reflect on the managed side.
@@ -411,6 +421,7 @@ public SavedFunctionState (LlvmIrFunction owner, ILlvmIrSavedFunctionSignatureSt
411421
public LlvmIrLinkage Linkage { get; set; } = LlvmIrLinkage.Default;
412422
public LlvmIrRuntimePreemption RuntimePreemption { get; set; } = LlvmIrRuntimePreemption.Default;
413423
public LlvmIrVisibility Visibility { get; set; } = LlvmIrVisibility.Default;
424+
public LlvmIrCallingConvention CallingConvention { get; set; } = LlvmIrCallingConvention.Default;
414425
public LlvmIrFunctionBody Body { get; }
415426
public string? Comment { get; set; }
416427
public bool ReturnsValue => Signature.ReturnType != typeof(void);

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,12 @@ partial class LlvmIrGenerator
4646
{ LlvmIrWritability.Constant, "constant" },
4747
{ LlvmIrWritability.Writable, "global" },
4848
};
49+
50+
// https://llvm.org/docs/LangRef.html#callingconv
51+
static readonly Dictionary<LlvmIrCallingConvention, string> llvmCallingConvention = new () {
52+
{ LlvmIrCallingConvention.Ccc, "ccc" },
53+
{ LlvmIrCallingConvention.Fastcc, "fastcc" },
54+
{ LlvmIrCallingConvention.Tailcc, "tailcc" },
55+
};
4956
}
5057
}

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ sealed class GeneratorWriteContext
2828
public readonly LlvmIrModuleTarget Target;
2929
public readonly LlvmIrMetadataManager MetadataManager;
3030
public readonly LlvmIrTypeCache TypeCache;
31+
public readonly LlvmIrGenerator Generator;
3132
public string CurrentIndent { get; private set; } = String.Empty;
3233
public bool InVariableGroup { get; set; }
3334
public LlvmIrVariableNumberFormat NumberFormat { get; set; } = LlvmIrVariableNumberFormat.Default;
3435

35-
public GeneratorWriteContext (TextWriter writer, LlvmIrModule module, LlvmIrModuleTarget target, LlvmIrMetadataManager metadataManager, LlvmIrTypeCache cache)
36+
public GeneratorWriteContext (LlvmIrGenerator generator, TextWriter writer, LlvmIrModule module, LlvmIrModuleTarget target, LlvmIrMetadataManager metadataManager, LlvmIrTypeCache cache)
3637
{
38+
Generator = generator;
3739
Output = writer;
3840
Module = module;
3941
Target = target;
@@ -163,7 +165,7 @@ public void Generate (TextWriter writer, LlvmIrModule module)
163165
LlvmIrMetadataManager metadataManager = module.GetMetadataManagerCopy ();
164166
target.AddTargetSpecificMetadata (metadataManager);
165167

166-
var context = new GeneratorWriteContext (writer, module, target, metadataManager, module.TypeCache);
168+
var context = new GeneratorWriteContext (this, writer, module, target, metadataManager, module.TypeCache);
167169
if (!String.IsNullOrEmpty (FilePath)) {
168170
WriteCommentLine (context, $" ModuleID = '{FileName}'");
169171
context.Output.WriteLine ($"source_filename = \"{FileName}\"");
@@ -670,7 +672,7 @@ string ToHex (BasicType basicTypeDesc, Type type, object? value)
670672
return $"{(basicTypeDesc.IsUnsigned ? prefixUnsigned : prefixSigned)}0x{hex}";
671673
}
672674

673-
void WriteValue (GeneratorWriteContext context, Type type, object? value)
675+
public void WriteValue (GeneratorWriteContext context, Type type, object? value)
674676
{
675677
if (value is LlvmIrVariable variableRef) {
676678
context.Output.Write (variableRef.Reference);
@@ -1226,6 +1228,11 @@ void WriteFunctionLeadingDecorations (GeneratorWriteContext context, LlvmIrFunct
12261228
context.Output.Write (llvmVisibility[func.Visibility]);
12271229
context.Output.Write (' ');
12281230
}
1231+
1232+
if (func.CallingConvention != LlvmIrCallingConvention.Default) {
1233+
context.Output.Write (llvmCallingConvention[func.CallingConvention]);
1234+
context.Output.Write (' ');
1235+
}
12291236
}
12301237

12311238
void WriteFunctionDeclarationTrailingDecorations (GeneratorWriteContext context, LlvmIrFunction func)
@@ -1251,6 +1258,10 @@ void WriteFunctionTrailingDecorations (GeneratorWriteContext context, LlvmIrFunc
12511258

12521259
public static void WriteReturnAttributes (GeneratorWriteContext context, LlvmIrFunctionSignature.ReturnTypeAttributes returnAttrs)
12531260
{
1261+
if (AttributeIsSet (returnAttrs.InReg)) {
1262+
context.Output.Write ("inreg");
1263+
}
1264+
12541265
if (AttributeIsSet (returnAttrs.NoUndef)) {
12551266
context.Output.Write ("noundef ");
12561267
}
@@ -1349,6 +1360,10 @@ public static void WriteParameterAttributes (GeneratorWriteContext context, Llvm
13491360
attributes.Add ("zeroext");
13501361
}
13511362

1363+
if (AttributeIsSet (parameter.WriteOnly)) {
1364+
attributes.Add ("writeonly");
1365+
}
1366+
13521367
if (parameter.Align.HasValue) {
13531368
attributes.Add ($"align({ValueOrPointerSize (parameter.Align.Value)})");
13541369
}

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

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,4 +624,109 @@ public Unreachable ()
624624
: base ("unreachable")
625625
{}
626626
}
627+
628+
public class Switch<T> : LlvmIrInstruction where T: struct
629+
{
630+
// Since we can't use System.Numerics.IBinaryInteger<T>, this is the poor man's verification that T is acceptable for us
631+
static readonly HashSet<Type> acceptedTypes = new () {
632+
typeof (byte),
633+
typeof (sbyte),
634+
typeof (short),
635+
typeof (ushort),
636+
typeof (int),
637+
typeof (uint),
638+
typeof (long),
639+
typeof (ulong),
640+
};
641+
642+
readonly LlvmIrVariable value;
643+
readonly LlvmIrFunctionLabelItem defaultDest;
644+
readonly string? automaticLabelPrefix;
645+
ulong automaticLabelCounter = 0;
646+
List<(T constant, LlvmIrFunctionLabelItem label, string? comment)>? items;
647+
648+
public Switch (LlvmIrVariable value, LlvmIrFunctionLabelItem defaultDest, string? automaticLabelPrefix = null)
649+
: base ("switch")
650+
{
651+
if (!acceptedTypes.Contains (typeof(T))) {
652+
throw new NotSupportedException ($"Type '{typeof(T)}' is unsupported, only integer types are accepted");
653+
}
654+
655+
if (value.Type != typeof (T)) {
656+
throw new ArgumentException ($"Must refer to value of type '{typeof(T)}'", nameof (value));
657+
}
658+
659+
this.value = value;
660+
this.defaultDest = defaultDest;
661+
this.automaticLabelPrefix = automaticLabelPrefix;
662+
663+
if (!String.IsNullOrEmpty (automaticLabelPrefix)) {
664+
items = new ();
665+
}
666+
}
667+
668+
protected override void WriteBody (GeneratorWriteContext context)
669+
{
670+
string irType = LlvmIrGenerator.MapToIRType (value.Type, context.TypeCache, out _, out bool isPointer);
671+
672+
context.Output.Write (irType);
673+
context.Output.Write (' ');
674+
675+
WriteValue (context, value.Type, value, isPointer);
676+
677+
context.Output.Write (", label %");
678+
context.Output.Write (defaultDest.Name);
679+
context.Output.WriteLine (" [");
680+
context.IncreaseIndent ();
681+
682+
foreach ((T constant, LlvmIrFunctionLabelItem label, string? comment) in items) {
683+
context.Output.Write (context.CurrentIndent);
684+
context.Output.Write (irType);
685+
context.Output.Write (' ');
686+
context.Generator.WriteValue (context, value.Type, constant);
687+
context.Output.Write (", label %");
688+
context.Output.Write (label.Name);
689+
if (!String.IsNullOrEmpty (comment)) {
690+
context.Generator.WriteCommentLine (context, comment);
691+
} else {
692+
context.Output.WriteLine ();
693+
}
694+
}
695+
696+
context.DecreaseIndent ();
697+
context.Output.Write (context.CurrentIndent);
698+
context.Output.Write (']');
699+
}
700+
701+
public LlvmIrFunctionLabelItem Add (T val, LlvmIrFunctionLabelItem? dest = null, string? comment = null)
702+
{
703+
var label = MakeLabel (dest);
704+
items.Add ((val, label, comment));
705+
return label;
706+
}
707+
708+
void EnsureValidity (LlvmIrFunctionLabelItem? dest)
709+
{
710+
if (dest != null) {
711+
return;
712+
}
713+
714+
if (String.IsNullOrEmpty (automaticLabelPrefix)) {
715+
throw new InvalidOperationException ($"Internal error: automatic label management requested, but prefix not defined");
716+
}
717+
}
718+
719+
LlvmIrFunctionLabelItem MakeLabel (LlvmIrFunctionLabelItem? maybeDest)
720+
{
721+
EnsureValidity (maybeDest);
722+
if (maybeDest != null) {
723+
return maybeDest;
724+
}
725+
726+
var ret = new LlvmIrFunctionLabelItem (automaticLabelCounter == 0 ? automaticLabelPrefix : $"{automaticLabelPrefix}{automaticLabelCounter}");
727+
automaticLabelCounter++;
728+
729+
return ret;
730+
}
731+
}
627732
}

0 commit comments

Comments
 (0)