Skip to content

Commit 26f1e74

Browse files
author
NielsDev
committed
Fully support interface virtual calls.
1 parent 2833dcb commit 26f1e74

File tree

12 files changed

+233
-76
lines changed

12 files changed

+233
-76
lines changed

CSharpLLVM/CSharpLLVM.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@
7171
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
7272
<Private>True</Private>
7373
</Reference>
74-
<Reference Include="std.swigged.llvm, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
75-
<HintPath>..\packages\swigged.llvm.4.0.5-alpha\lib\netstandard1.1\std.swigged.llvm.dll</HintPath>
74+
<Reference Include="swigged.llvm, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
75+
<HintPath>..\packages\swigged.llvm.4.0.10\lib\netstandard1.1\swigged.llvm.dll</HintPath>
7676
<Private>True</Private>
7777
</Reference>
7878
<Reference Include="System" />

CSharpLLVM/Compilation/BuiltinRuntimeFunctions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ private void createStringGetCharsFunction(BuilderRef builder)
3737
// Create function and set linkage.
3838
TypeRef type = LLVM.FunctionType(TypeHelper.Int8, new TypeRef[] { TypeHelper.String, TypeHelper.Int32 }, false);
3939
ValueRef func = LLVM.AddFunction(mCompiler.Module, funcName, type);
40-
LLVM.SetLinkage(func, Linkage.InternalLinkage);
40+
LLVM.SetLinkage(func, Linkage.LinkOnceAnyLinkage);
4141
LLVM.AddAttributeAtIndex(func, (uint)LLVM.AttributeFunctionIndex, LLVM.CreateEnumAttribute(mCompiler.ModuleContext, LLVM.GetEnumAttributeKindForName("alwaysinline", 12), 1));
4242

4343
// Arguments.

CSharpLLVM/Compilation/Compiler.cs

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using Mono.Cecil;
2-
using Mono.Collections.Generic;
32
using Swigged.LLVM;
43
using System;
54
using System.Collections.Generic;
@@ -82,15 +81,15 @@ public void Compile(string moduleName)
8281
LLVM.InitializeAllTargetMCs();
8382
LLVM.InitializeAllAsmParsers();
8483
LLVM.InitializeAllAsmPrinters();
85-
84+
8685
string triplet = (Options.Target == "default") ? LLVM.GetDefaultTargetTriple() : Options.Target;
87-
string error;
88-
86+
MyString error = new MyString();
87+
8988
LLVM.SetTarget(mModule, triplet);
9089
TargetRef target;
91-
if (LLVM.GetTargetFromTriple(triplet, out target, out error))
90+
if (LLVM.GetTargetFromTriple(triplet, out target, error))
9291
{
93-
throw new InvalidOperationException(error);
92+
throw new InvalidOperationException(error.ToString());
9493
}
9594

9695
// Optimizer.
@@ -175,10 +174,10 @@ public void Compile(string moduleName)
175174

176175
// Verify and throw exception on error.
177176
Console.ForegroundColor = ConsoleColor.DarkGray;
178-
if (LLVM.VerifyModule(mModule, VerifierFailureAction.ReturnStatusAction, out error))
177+
if (LLVM.VerifyModule(mModule, VerifierFailureAction.ReturnStatusAction, error))
179178
{
180179
Console.WriteLine("Compilation of module failed.");
181-
Console.WriteLine(error);
180+
Console.WriteLine(error.ToString());
182181
LLVM.DisposeTargetData(TargetData);
183182
Console.ForegroundColor = ConsoleColor.Gray;
184183
return;
@@ -187,7 +186,6 @@ public void Compile(string moduleName)
187186
{
188187
Console.WriteLine("Compilation of module succeeded.");
189188
}
190-
Console.ForegroundColor = ConsoleColor.Gray;
191189

192190
// Output assembly or object file.
193191
if (!Options.OutputLLVM)
@@ -196,23 +194,24 @@ public void Compile(string moduleName)
196194
LLVM.SetModuleDataLayout(mModule, LLVM.CreateTargetDataLayout(machine));
197195
CodeGenFileType type = (Options.OutputAssembly) ? CodeGenFileType.AssemblyFile : CodeGenFileType.ObjectFile;
198196

199-
if (LLVM.TargetMachineEmitToFile(machine, mModule, Options.OutputFile, type, out error))
197+
if (LLVM.TargetMachineEmitToFile(machine, mModule, Options.OutputFile, type, error))
200198
{
201-
throw new InvalidOperationException(error);
199+
throw new InvalidOperationException(error.ToString());
202200
}
203201
}
204202
// Output LLVM code to a file.
205203
else
206204
{
207-
if (LLVM.PrintModuleToFile(mModule, Options.OutputFile, out error))
205+
if (LLVM.PrintModuleToFile(mModule, Options.OutputFile, error))
208206
{
209207
Console.WriteLine("Writing the LLVM code to a file failed.");
210-
Console.WriteLine(error);
208+
Console.WriteLine(error.ToString());
211209
Console.ForegroundColor = ConsoleColor.Gray;
212210
}
213211
}
214212

215213
// Cleanup.
214+
Console.ForegroundColor = ConsoleColor.Gray;
216215
LLVM.DisposeTargetData(TargetData);
217216
}
218217

@@ -226,8 +225,7 @@ private void compileModules()
226225
// Loop through the modules within the IL assembly.
227226
// Note: A single assembly can contain multiple IL modules.
228227
// We use a single LLVM module to contain all of this.
229-
Collection<ModuleDefinition> modules = AssemblyDef.Modules;
230-
foreach (ModuleDefinition moduleDef in modules)
228+
foreach (ModuleDefinition moduleDef in AssemblyDef.Modules)
231229
{
232230
compileModule(moduleDef);
233231
}
@@ -253,6 +251,7 @@ private void compileInitCctorsMethod()
253251
}
254252

255253
LLVM.BuildRetVoid(builder);
254+
256255
LLVM.DisposeBuilder(builder);
257256
}
258257

@@ -318,12 +317,6 @@ private void compileModule(ModuleDefinition moduleDef)
318317
if (method.Name == ".cctor")
319318
Lookup.AddCctor(method);
320319
}
321-
322-
// Compile VTables.
323-
foreach (VTable vtable in Lookup.VTables)
324-
{
325-
vtable.Compile();
326-
}
327320
}
328321

329322
/// <summary>

CSharpLLVM/Compilation/MethodCompiler.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public void CreateDeclaration(MethodDefinition methodDef)
4848
if (methodDef.HasThis)
4949
{
5050
argTypes[0] = TypeHelper.GetTypeRefFromType(methodDef.DeclaringType);
51+
52+
// We need to pass the valuetype as a pointer because we need to modify its contents in the constructor.
5153
if (methodDef.DeclaringType.IsValueType)
5254
argTypes[0] = LLVM.PointerType(argTypes[0], 0);
5355
}
@@ -70,7 +72,7 @@ public void Compile(MethodDefinition methodDef)
7072
// A method has internal linkage if one (or both) of the following is true:
7173
// 1) The method is a private method.
7274
// 2) The compiler got an option to set the linkage of instance methods to internal.
73-
if (methodDef.IsPrivate || (!methodDef.IsStatic && mCompiler.Options.InstanceMethodInternalLinkage))
75+
if (!methodDef.IsStatic && (methodDef.IsPrivate || mCompiler.Options.InstanceMethodInternalLinkage))
7476
{
7577
LLVM.SetLinkage(function.Value, Linkage.InternalLinkage);
7678
if (mCompiler.Options.InternalMethodsFastCC)

CSharpLLVM/Compilation/MethodContext.cs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public BasicBlockRef GetBlockOf(Instruction instr)
6161
{
6262
return GetBranch(instr).Block;
6363
}
64-
64+
6565
/// <summary>
6666
/// Creates the branches.
6767
/// </summary>
@@ -70,12 +70,9 @@ private void createBranches()
7070
// Look for branching, create blocks for the branches.
7171
// Note: First, we search all the branches so we can later add them in the correct order.
7272
// This is because we may not always have the branches in a chronological order.
73-
bool[] isNewBranch = new bool[Method.Body.CodeSize];
7473
int[] refers = new int[Method.Body.CodeSize];
75-
76-
isNewBranch[0] = true;
77-
7874
Branches = new Branch[Method.Body.CodeSize];
75+
Branches[0] = new Branch(this, 0);
7976

8077
foreach (Instruction instruction in Method.Body.Instructions)
8178
{
@@ -85,7 +82,8 @@ private void createBranches()
8582
if (flow == FlowControl.Branch || flow == FlowControl.Cond_Branch)
8683
{
8784
Instruction dest = (Instruction)instruction.Operand;
88-
isNewBranch[dest.Offset] = true;
85+
if (Branches[dest.Offset] == null)
86+
Branches[dest.Offset] = new Branch(this, dest.Offset);
8987
refers[dest.Offset]++;
9088
}
9189

@@ -94,18 +92,18 @@ private void createBranches()
9492
// If this instruction does branching by a conditional, we also need to have a block after this instruction,
9593
// for if the conditional branch is not being executed.
9694
if (flow == FlowControl.Cond_Branch)
97-
isNewBranch[instruction.Next.Offset] = true;
95+
{
96+
if (Branches[instruction.Next.Offset] == null)
97+
{
98+
Branches[instruction.Next.Offset] = new Branch(this, instruction.Next.Offset);
99+
}
100+
}
98101
else if (flow == FlowControl.Next)
102+
{
99103
refers[instruction.Next.Offset]++;
104+
}
100105
}
101106
}
102-
103-
// Create branches.
104-
for (int i = 0; i < Branches.Length; i++)
105-
{
106-
if (isNewBranch[i])
107-
Branches[i] = new Branch(this, i);
108-
}
109107

110108
// Now that we know the reference count and where to put branches, let's create them.
111109
// For more explanation: refer to the loop above.
@@ -114,7 +112,7 @@ private void createBranches()
114112
{
115113
FlowControl flow = instruction.OpCode.FlowControl;
116114

117-
if (isNewBranch[instruction.Offset])
115+
if (Branches[instruction.Offset] != null)
118116
{
119117
current = Branches[instruction.Offset];
120118
}

CSharpLLVM/Compilation/Options.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using CommandLine;
22
using CommandLine.Text;
3-
using Swigged.LLVM;
43
using System.Reflection;
54

65
namespace CSharpLLVM.Compilation

CSharpLLVM/Compilation/TypeCompiler.cs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ enum TypeKind
1717
Interface
1818
}
1919

20-
private static readonly ConsoleColor[] ColorLookup =
20+
private static readonly ConsoleColor[] TypeKindColorLookup =
2121
{
2222
ConsoleColor.DarkCyan,
2323
ConsoleColor.DarkGreen,
@@ -92,7 +92,7 @@ public void Compile(TypeDefinition type)
9292
// Log.
9393
if (mCompiler.Options.Verbose)
9494
{
95-
Console.ForegroundColor = ColorLookup[(int)typeKind];
95+
Console.ForegroundColor = TypeKindColorLookup[(int)typeKind];
9696
Console.WriteLine(string.Format("Compiling type {0}", type.FullName));
9797
Console.ForegroundColor = ConsoleColor.Gray;
9898
}
@@ -101,6 +101,10 @@ public void Compile(TypeDefinition type)
101101
if (typeKind == TypeKind.Enum)
102102
return;
103103

104+
// Add interface type to lookup?
105+
if (typeKind == TypeKind.Interface)
106+
mLookup.AddInterface(type);
107+
104108
// VTable.
105109
VTable vtable = null;
106110
bool hasVTable = ((typeKind == TypeKind.Class || typeKind == TypeKind.Interface) && mCompiler.Lookup.NeedsVirtualCall(type));
@@ -109,6 +113,7 @@ public void Compile(TypeDefinition type)
109113
vtable = new VTable(mCompiler, type);
110114
mLookup.AddVTable(vtable);
111115
vtable.Create();
116+
vtable.Compile();
112117
}
113118

114119
// Create struct for this type.
@@ -127,14 +132,13 @@ public void Compile(TypeDefinition type)
127132
// Only if there are virtual calls on this type.
128133
if (hasVTable)
129134
{
130-
VTableEntry barrier = (VTableEntry)entry;
131-
structData.Add(LLVM.PointerType(vtable.GetEntry(barrier.Type).Item1, 0));
135+
VTableEntry castedEntry = (VTableEntry)entry;
136+
structData.Add(LLVM.PointerType(vtable.GetEntry(castedEntry.Type).Item1, 0));
132137
}
133138
}
134139
// Entry that points to a table of VTables for interfaces
135140
else if (entry.EntryType == StructEntryType.InterfaceVTablesTable)
136141
{
137-
// TODO
138142
structData.Add(TypeHelper.VoidPtr);
139143
}
140144
// Field entry
@@ -179,6 +183,7 @@ public void Compile(TypeDefinition type)
179183
if (typeKind != TypeKind.Struct)
180184
throw new InvalidOperationException("Fixed size not on a struct?!");
181185

186+
// Add bytes until the needed size is reached.
182187
int needed = type.ClassSize - (int)fieldTotalSize;
183188
for (int i = 0; i < needed; i++)
184189
structData.Add(TypeHelper.Int8);
@@ -217,7 +222,7 @@ private ValueRef createNewobjMethod(TypeDefinition type)
217222
TypeRef typeRef = mCompiler.Lookup.GetTypeRef(type);
218223
ValueRef objPtr = LLVM.BuildMalloc(builder, typeRef, "newobj");
219224

220-
// Initialize VTables.
225+
// Initialize class VTables.
221226
Lookup lookup = mCompiler.Lookup;
222227
if (lookup.NeedsVirtualCall(type))
223228
{
@@ -231,6 +236,14 @@ private ValueRef createNewobjMethod(TypeDefinition type)
231236
}
232237
}
233238

239+
// Initialize interface indirection table.
240+
if (type.HasInterfaces)
241+
{
242+
Tuple<TypeRef, ValueRef> indirectionTable = mLookup.GetInterfaceIndirectionTable(type);
243+
ValueRef gep = LLVM.BuildInBoundsGEP(builder, objPtr, new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, 0, false), LLVM.ConstInt(TypeHelper.Int32, 0, false) }, "interfaceindirectiontablegep");
244+
LLVM.BuildStore(builder, LLVM.BuildPointerCast(builder, indirectionTable.Item2, TypeHelper.VoidPtr, string.Empty), gep);
245+
}
246+
234247
// Return object pointer.
235248
LLVM.BuildRet(builder, objPtr);
236249

CSharpLLVM/Generator/Instructions/FlowControl/EmitCallvirt.cs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using CSharpLLVM.Stack;
66
using CSharpLLVM.Compilation;
77
using CSharpLLVM.Lookups;
8+
using System;
89

910
namespace CSharpLLVM.Generator.Instructions.FlowControl
1011
{
@@ -22,13 +23,14 @@ public void Emit(Instruction instruction, MethodContext context, BuilderRef buil
2223
MethodReference methodRef = (MethodReference)instruction.Operand;
2324
TypeRef returnType = TypeHelper.GetTypeRefFromType(methodRef.ReturnType);
2425
bool needsVirtualCall = context.Compiler.Lookup.NeedsVirtualCall(methodRef.DeclaringType);
26+
Lookup lookup = context.Compiler.Lookup;
2527

2628
// Build parameter value and types arrays.
2729
int paramCount = 1 + methodRef.Parameters.Count;
2830

2931
// Get the method, if it is null, create a new empty one, otherwise reference it.
3032
string methodName = NameHelper.CreateMethodName(methodRef);
31-
ValueRef? func = context.Compiler.Lookup.GetFunction(methodName);
33+
ValueRef? func = lookup.GetFunction(methodName);
3234

3335
// Process arguments.
3436
// Note: backwards for loop because stack is backwards!
@@ -61,19 +63,41 @@ public void Emit(Instruction instruction, MethodContext context, BuilderRef buil
6163
TypeRef functionType = LLVM.FunctionType(returnType, paramTypes, false);
6264

6365
// Call.
64-
Lookup lookup = context.Compiler.Lookup;
6566
ValueRef method;
6667
if (needsVirtualCall && !lookup.IsMethodUnique(methodRef))
6768
{
6869
// We need a virtual call.
70+
TypeDefinition methodParent = methodRef.DeclaringType.Resolve();
6971
TypeRef funcPtrType = LLVM.PointerType(functionType, 0);
70-
VTable vTable = lookup.GetVTable(methodRef.DeclaringType);
71-
uint index = lookup.GetClassVTableIndex(methodRef.DeclaringType.Resolve());
72+
VTable vTable = lookup.GetVTable(methodParent);
73+
ValueRef methodGep;
74+
75+
// Two cases:
76+
// 1) The parent of the method is an interface.
77+
// In this case, we need to first get the VTable from the indirection table.
78+
// 2) The parent of the method is a class.
79+
// In this case, we can directly now the offset to the VTable from the object pointer.
80+
81+
if (!methodParent.IsInterface)
82+
{
83+
uint index = lookup.GetClassVTableIndex(methodParent);
84+
ValueRef vTableGep = LLVM.BuildInBoundsGEP(builder, argVals[0], new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, 0, false), LLVM.ConstInt(TypeHelper.Int32, index, false) }, "vtablegep");
85+
ValueRef vTableInstance = LLVM.BuildLoad(builder, vTableGep, "vtable");
86+
methodGep = LLVM.BuildInBoundsGEP(builder, vTableInstance, new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, 0, false), LLVM.ConstInt(TypeHelper.Int32, (uint)vTable.GetMethodIndex(methodParent, methodRef), false) }, "methodgep");
87+
}
88+
else
89+
{
90+
uint index = lookup.GetInterfaceID(methodParent);
91+
92+
ValueRef indirectionGep = LLVM.BuildInBoundsGEP(builder, argVals[0], new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, 0, false) }, "indirectiongep");
93+
indirectionGep = LLVM.BuildPointerCast(builder, indirectionGep, LLVM.PointerType(LLVM.PointerType(LLVM.PointerType(TypeHelper.VoidPtr, 0), 0), 0), string.Empty);
94+
ValueRef indirectionTable = LLVM.BuildLoad(builder, indirectionGep, "indirectiontable");
95+
ValueRef vTableGep = LLVM.BuildInBoundsGEP(builder, indirectionTable, new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, index, false) }, "vtablegep");
96+
ValueRef vTableInstance = LLVM.BuildLoad(builder, vTableGep, "vtable");
97+
methodGep = LLVM.BuildInBoundsGEP(builder, vTableInstance, new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, (uint)vTable.GetMethodIndex(methodParent, methodRef), false) }, "methodgep");
98+
}
7299

73-
// Get a function pointer.
74-
ValueRef vTableGep = LLVM.BuildInBoundsGEP(builder, argVals[0], new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, 0, false), LLVM.ConstInt(TypeHelper.Int32, index, false) }, "vtablegep");
75-
ValueRef vTableInstance = LLVM.BuildLoad(builder, vTableGep, "vtable");
76-
ValueRef methodGep = LLVM.BuildInBoundsGEP(builder, vTableInstance, new ValueRef[] { LLVM.ConstInt(TypeHelper.Int32, 0, false), LLVM.ConstInt(TypeHelper.Int32, (uint)vTable.GetMethodIndex(methodRef.DeclaringType.Resolve(), methodRef), false) }, "methodptr");
100+
// Indirect call.
77101
ValueRef methodPtr = LLVM.BuildLoad(builder, methodGep, "methodptr");
78102
method = LLVM.BuildPointerCast(builder, methodPtr, funcPtrType, "method");
79103
}

0 commit comments

Comments
 (0)