@@ -18,91 +18,15 @@ limitations under the License.
1818#include " mlir/Transforms/DialectConversion.h"
1919#include " rlc/dialect/ActionArgumentAnalysis.hpp"
2020#include " rlc/dialect/Dialect.h"
21+ #include " rlc/dialect/MemberFunctionsTable.hpp"
2122#include " rlc/dialect/Operations.hpp"
2223#include " rlc/dialect/Passes.hpp"
2324#include " rlc/dialect/Types.hpp"
2425#include " rlc/dialect/Visits.hpp"
2526#include " rlc/utils/PatternMatcher.hpp"
2627namespace mlir ::rlc
2728{
28- // a table that keeps track of which member functions belong to which type
29- class MemberFunctionsTable
30- {
31- public:
32- MemberFunctionsTable (mlir::ModuleOp mod)
33- {
34- for (auto op : mod.getOps <mlir::rlc::FunctionOp>())
35- if (op.getIsMemberFunction () and not op.isInternal () and
36- (op.getArgumentTypes ()[0 ].isa <mlir::rlc::ClassType>() or
37- op.getArgumentTypes ()[0 ].isa <mlir::rlc::AlternativeType>()))
38- {
39- auto selfType = op.getArgumentTypes ()[0 ];
40- auto key = selfType.getAsOpaquePointer ();
41- if (isInitFunction (selfType, op))
42- initFunction[key] = op;
43- else if (isDropFunction (selfType, op))
44- dropFunction[key] = op;
45- else if (isAssignFunction (selfType, op))
46- assignFunction[key] = op;
47- else
48- typeToMethods[key].insert (op);
49- }
50- }
51-
52- bool isInitFunction (mlir::Type t, mlir::rlc::FunctionOp method)
53- {
54- return (
55- method.getUnmangledName () == " init" and
56- returnsVoid (method.getType ()).succeeded () and
57- method.getType ().getNumInputs () == 1 and
58- method.getType ().getInput (0 ) == t);
59- }
60-
61- bool isTriviallyInitializable (mlir::Type t)
62- {
63- return initFunction.count (t.getAsOpaquePointer ()) == 0 ;
64- }
65-
66- bool isDropFunction (mlir::Type t, mlir::rlc::FunctionOp method)
67- {
68- return (
69- method.getUnmangledName () == " drop" and
70- returnsVoid (method.getType ()).succeeded () and
71- method.getType ().getNumInputs () == 1 and
72- method.getType ().getInput (0 ) == t);
73- }
74-
75- bool isTriviallyDestructible (mlir::Type t)
76- {
77- return dropFunction.count (t.getAsOpaquePointer ()) == 0 ;
78- }
79-
80- bool isAssignFunction (mlir::Type t, mlir::rlc::FunctionOp method)
81- {
82- return (
83- method.getUnmangledName () == " assign" and
84- returnsVoid (method.getType ()).succeeded () and
85- method.getType ().getNumInputs () == 2 and
86- method.getType ().getInput (0 ) == t and
87- method.getType ().getInput (1 ) == t);
88- }
89-
90- bool isTriviallyCopiable (mlir::Type t)
91- {
92- return assignFunction.count (t.getAsOpaquePointer ()) == 0 ;
93- }
94-
95- llvm::DenseSet<mlir::rlc::FunctionOp> getMemberFunctionsOf (mlir::Type type)
96- {
97- return typeToMethods[type.getAsOpaquePointer ()];
98- }
99-
100- private:
101- std::map<const void *, llvm::DenseSet<mlir::rlc::FunctionOp>> typeToMethods;
102- std::map<const void *, mlir::rlc::FunctionOp> initFunction;
103- std::map<const void *, mlir::rlc::FunctionOp> dropFunction;
104- std::map<const void *, mlir::rlc::FunctionOp> assignFunction;
105- };
29+ // Emits python imports and bookinping global variables.
10630 static void printPrelude (StreamWriter& writer, bool isMac, bool isWindows)
10731 {
10832 writer.writenl (" import ctypes" );
@@ -129,6 +53,8 @@ namespace mlir::rlc
12953 writer.endLine ();
13054 }
13155
56+ // returns true if `type` has a immediate mapping onto a ctypes
57+ // type.
13258 static bool builtinCType (mlir::Type type)
13359 {
13460 if (auto casted = mlir::dyn_cast<mlir::rlc::FrameType>(type))
@@ -141,6 +67,8 @@ namespace mlir::rlc
14167 mlir::isa<mlir::rlc::StringLiteralType>(type);
14268 }
14369
70+ // returns true if the require holds the real content
71+ // in the `.value` member
14472 static bool needsUnwrapping (mlir::FunctionType type)
14573 {
14674 if (type.getNumResults () == 0 )
@@ -252,7 +180,7 @@ namespace mlir::rlc
252180 printCallArgs (type.getInputs (), argsInfo, w, resultType);
253181
254182 // for functions that return something emit
255- // return result
183+ // return __result
256184 // and if they return a builtin ctype type, add .value to
257185 // extract to convert it to a python builtin type instead
258186 if (returnsVoid (type).failed ())
@@ -280,6 +208,18 @@ namespace mlir::rlc
280208 w.indentOnce (1 ).writenl (" return" ).endLine ();
281209 }
282210
211+ // When we declare a python function we
212+ // * Emit the mangled function
213+ // (eg: def rl_ugly_mangled_name_r_bool(arg: ctypes.c_bool):)
214+ // that dispaches directly to the symbol in the binary
215+ //
216+ // * Emit the non mangled typehinting
217+ // @overload
218+ // def nice_name(arg: Bool):
219+ // return
220+ //
221+ // * stick that overload in the signatures global list so
222+ // other people can discover it dynamically if they need.
283223 static void declarePythonFunction (
284224 llvm::StringRef unmangledName,
285225 llvm::ArrayRef<llvm::StringRef> argsInfo,
@@ -297,7 +237,6 @@ namespace mlir::rlc
297237 auto mangledName =
298238 mlir::rlc::mangledName (unmangledName, isMemberFunction, type);
299239
300- // mangled wrapper
301240 printMangledWrapper (
302241 unmangledName,
303242 mangledName,
@@ -320,6 +259,12 @@ namespace mlir::rlc
320259 w.writenl (" ]" ).endLine ();
321260 }
322261
262+ // the three rlc special functions init, drop and assing must
263+ // be special cased to ensure they are always called, otherwise
264+ // the user could access invalid memory.
265+ //
266+ // In practice this means overriding python __init__, __del__ and
267+ // clone so that we can dispatch to the proper rlc methods.
323268 void emitSpecialFunctions (
324269 mlir::Type type, mlir::rlc::StreamWriter& w, MemberFunctionsTable& table)
325270 {
@@ -370,6 +315,8 @@ namespace mlir::rlc
370315 }
371316 }
372317
318+ // emits the ctypes __fields__ member that specifies
319+ // the layout of the class.
373320 void emitMembers (
374321 llvm::ArrayRef<mlir::Type> types,
375322 llvm::ArrayRef<llvm::StringRef> memberNames,
@@ -419,6 +366,16 @@ namespace mlir::rlc
419366 w.endLine ();
420367 }
421368
369+ // Python has no innate overload, so we have to create runtime
370+ // dispatchers that look at the types of arguments and find the
371+ // right overload.
372+ //
373+ // In practice this just means doing
374+ // def f(*args):
375+ // if len(args) == overload_arg_count and isinstance(args[0], t1) and ...:
376+ // return overload(*args)
377+ // ...
378+ // raise TypeError()
422379 void emitOverloadDispatcher (
423380 llvm::StringRef name,
424381 llvm::ArrayRef<mlir::FunctionType> overloads,
@@ -544,6 +501,15 @@ namespace mlir::rlc
544501 }
545502 };
546503
504+ // ActionFunction end up generating:
505+ // * A class that rappresents the ActionFunction
506+ // * A free function to start the ActionFunction
507+ // * Optionally, the precondition function of the free function.
508+ // * the is_done member function.
509+ //
510+ // then, for each actions statement:
511+ // * emit the member function to trigger that action
512+ // * emit the precondition of that action member function.
547513 class ActionToPythonFunction
548514 {
549515 private:
@@ -740,6 +706,9 @@ namespace mlir::rlc
740706 registerCommonTypeConversion (ser);
741707 }
742708
709+ // emitting a action function is ugly because we need to collect
710+ // the frame type from the action function, and the arguments from
711+ // the action statements.
743712 static std::string emitActionFunction (
744713 mlir::rlc::ClassType frameType,
745714 llvm::StringRef actionName,
@@ -863,6 +832,38 @@ namespace mlir::rlc
863832
864833#define GEN_PASS_DEF_PRINTPYTHONPASS
865834#include " rlc/dialect/Passes.inc"
835+ /* **
836+ *This pass emits a python module that describes the content of the rlc
837+ *module being compiled. It does the following:
838+ * 1 For each ClassType and AlternativeType in the RLC program it emits a
839+ * CType structure or union that with same members, and makes sure that
840+ * constructors, clone and destructor invoke the right method in the RLC
841+ * shared library.
842+ *
843+ *
844+ * 2 For each alias it emits the same alias
845+ *
846+ * 3 For each free function it emits a ctypes function declaration that wraps
847+ * that RLC function, annotated with the real signature of the function. The
848+ * emitted function has the same mangled name as the function in the
849+ * library.
850+ *
851+ * Then, it emits a dispatcher function that accepts a argument list and
852+ * invokes the correct overload according to the types of the variables the
853+ * python user has provided.
854+ *
855+ * Finally it emits typehinting annotations so that the user can see the
856+ * proper overloads available for a free function
857+ *
858+ * 4 For each member function it does the same thing that it did in step 3,
859+ * but instead of putting it into the global name space, the generated stuff
860+ * is placed inside the class that own the free function
861+ *
862+ * 5 For each ActionFunction it emits the ActionFunction as a ctypes class,
863+ * and inside that class it emits the ctypes function wrappers to invoke
864+ * the is_done function and actions statements that the ActionFunction
865+ * declares.
866+ ***/
866867 struct PrintPythonPass : impl::PrintPythonPassBase<PrintPythonPass>
867868 {
868869 using impl::PrintPythonPassBase<PrintPythonPass>::PrintPythonPassBase;
0 commit comments