/
CodeGenerator.cs
225 lines (189 loc) · 7.9 KB
/
CodeGenerator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// -----------------------------------------------------------------------------
// ILGPU
// Copyright (c) 2016-2018 Marcel Koester
// www.ilgpu.net
//
// File: CodeGenerator.cs
//
// This file is part of ILGPU and is distributed under the University of
// Illinois Open Source License. See LICENSE.txt for details
// -----------------------------------------------------------------------------
using ILGPU.LLVM;
using ILGPU.Resources;
using ILGPU.Util;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using static ILGPU.LLVM.LLVMMethods;
namespace ILGPU.Compiler
{
/// <summary>
/// Represents a LLVM code generator for .Net methods.
/// </summary>
/// <remarks>Members of this class are not thread safe.</remarks>
sealed partial class CodeGenerator : DisposeBase, IBasicBlockHost
{
#region Instance
/// <summary>
/// The disassembled method for the current code generator.
/// </summary>
private readonly DisassembledMethod disassembledMethod;
/// <summary>
/// Stores processed basic blocks.
/// </summary>
private readonly HashSet<BasicBlock> processedBasicBlocks = new HashSet<BasicBlock>();
/// <summary>
/// Constructs a new code generator that targets the given unit.
/// </summary>
/// <param name="unit">The target unit.</param>
/// <param name="method">The source method for code generation.</param>
/// <param name="disassembledMethod">The disassembled method for code generation.</param>
public CodeGenerator(
CompileUnit unit,
Method method,
DisassembledMethod disassembledMethod = null)
{
Debug.Assert(unit != null, "Invalid unit");
Debug.Assert(method != null, "Invalid method");
Unit = unit;
Method = method;
disassembledMethod = disassembledMethod ??
DisassembledMethod.Disassemble(method.MethodBase, CompilationContext.NotSupportedILInstructionHandler);
if (disassembledMethod.Method.GetMethodBody().ExceptionHandlingClauses.Count > 0)
throw CompilationContext.GetNotSupportedException(
ErrorMessages.CustomExceptionSemantics, method.Name);
Debug.Assert(
method.MethodBase == disassembledMethod.Method,
"The provided disassembled method does not match the given method for code generation");
this.disassembledMethod = disassembledMethod;
Builder = CreateBuilderInContext(unit.LLVMContext);
InitCFG();
InitArgsAndLocals();
}
#endregion
#region Properties
/// <summary>
/// Returns the assigned compile unit.
/// </summary>
public CompileUnit Unit { get; }
/// <summary>
/// Returns the source method.
/// </summary>
public Method Method { get; }
/// <summary>
/// Returns the assigned instruction builder.
/// </summary>
public LLVMBuilderRef Builder { get; private set; }
/// <summary>
/// Returns the current compilation context.
/// </summary>
public CompilationContext CompilationContext => Unit.CompilationContext;
#endregion
#region Private Properties
/// <summary>
/// Returns the current method base.
/// </summary>
private MethodBase MethodBase => Method.MethodBase;
/// <summary>
/// Returns the associated LLVM context.
/// </summary>
private LLVMContextRef LLVMContext => Unit.LLVMContext;
/// <summary>
/// Returns the associated LLVM function.
/// </summary>
private LLVMValueRef Function => Method.LLVMFunction;
/// <summary>
/// Returns the entry block of the function.
/// </summary>
private BasicBlock EntryBlock { get; set; }
/// <summary>
/// Returns the current block.
/// </summary>
private BasicBlock CurrentBlock { get; set; }
#endregion
#region Methods
/// <summary>
/// Generates LLVM code for the current method.
/// </summary>
public void GenerateCode()
{
// Seal the entry block
EntryBlock.Seal();
// Generate code for all blocks
processedBasicBlocks.Clear();
for (int blockIdx = postOrder.Count - 1; blockIdx >= 0; --blockIdx)
GenerateCodeForBlock(postOrder[blockIdx]);
}
/// <summary>
/// Returns true iff the given block was already preprocessed.
/// </summary>
/// <param name="block">The block to check.</param>
/// <returns>True, iff the given block was already preprocessed.</returns>
public bool IsProcessed(BasicBlock block)
{
return processedBasicBlocks.Contains(block);
}
/// <summary>
/// Generates code for the given basic block.
/// </summary>
/// <param name="block">The block to generate code for.</param>
private void GenerateCodeForBlock(BasicBlock block)
{
if (!block.IsSealed && block.Predecesors.All(processedBasicBlocks.Contains))
block.Seal();
if (processedBasicBlocks.Contains(block))
return;
processedBasicBlocks.Add(block);
CurrentBlock = block;
PositionBuilderAtEnd(Builder, CurrentBlock.LLVMBlock);
for (int i = block.InstructionOffset, e = block.InstructionOffset + block.InstructionCount; i < e; ++i)
{
var instruction = disassembledMethod[i];
if (!InstructionHandlers.TryGetValue(instruction.InstructionType, out InstructionHandler instructionHandler))
throw CompilationContext.GetNotSupportedException(
ErrorMessages.NotSupportedInstruction, Method.Name, instruction);
instructionHandler(this, instruction);
}
// Handle implicit cases in which there is no explicit jump operation to a single successor
if (block.Successors.Count == 1 && block.InstructionCount > 0)
{
var successor = block.Successors.First();
var lastInstruction = disassembledMethod[block.InstructionOffset + block.InstructionCount - 1];
if (!lastInstruction.IsTerminator)
BuildBr(Builder, successor.LLVMBlock);
}
}
/// <summary>
/// Creates a temporary alloca instruction that allocates a temp
/// storage of the given type in the entry block.
/// </summary>
/// <param name="llvmType">The type of the temporary to allocate.</param>
/// <returns>The allocated alloca instruction.</returns>
private LLVMValueRef CreateTempAlloca(LLVMTypeRef llvmType)
{
var currentBlock = GetInsertBlock(Builder);
var firstInstruction = GetFirstInstruction(EntryBlock.LLVMBlock);
if (firstInstruction.Pointer != IntPtr.Zero)
PositionBuilderBefore(Builder, firstInstruction);
else
PositionBuilderAtEnd(Builder, EntryBlock.LLVMBlock);
var alloca = BuildAlloca(Builder, llvmType, string.Empty);
PositionBuilderAtEnd(Builder, currentBlock);
return alloca;
}
#endregion
#region IDisposable
/// <summary cref="DisposeBase.Dispose(bool)"/>
protected override void Dispose(bool disposing)
{
if (Builder.Pointer != IntPtr.Zero)
{
DisposeBuilder(Builder);
Builder = default(LLVMBuilderRef);
}
}
#endregion
}
}