1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2012-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if ! defined (octave_jit_typeinfo_h)
27 #define octave_jit_typeinfo_h 1
28 
29 #include "octave-config.h"
30 
31 #if defined (HAVE_LLVM)
32 
33 #include <map>
34 #include <vector>
35 
36 #include "Range.h"
37 #include "jit-util.h"
38 
39 namespace octave
40 {
41   class jit_typeinfo;
42   class jit_module;
43 
44   // Defines the type system used by jit and a singleton class, jit_typeinfo, to
45   // manage the types.
46   //
47   // FIXME:
48   // Operations are defined and implemented in jit_typeinfo.  Eventually they
49   // should be moved elsewhere. (just like with octave_typeinfo)
50 
51   // jit_range is compatible with the llvm range structure
52   struct
53   jit_range
54   {
jit_rangejit_range55     jit_range (const Range& from)
56       : m_base (from.base ()), m_limit (from.limit ()), m_inc (from.inc ()),
57         m_nelem (from.numel ())
58     { }
59 
Rangejit_range60     operator Range () const
61     {
62       return Range (m_base, m_limit, m_inc);
63     }
64 
65     bool all_elements_are_ints (void) const;
66 
67     double m_base;
68     double m_limit;
69     double m_inc;
70     octave_idx_type m_nelem;
71   };
72 
73   std::ostream& operator << (std::ostream& os, const jit_range& rng);
74 
75   // jit_array is compatible with the llvm array/matrix structures
76   template <typename T, typename U>
77   struct
78   jit_array
79   {
jit_arrayjit_array80     jit_array (void) : m_array (0) { }
81 
jit_arrayjit_array82     jit_array (T& from) : m_array (new T (from))
83     {
84       update ();
85     }
86 
updatejit_array87     void update (void)
88     {
89       m_ref_count = m_array->jit_ref_count ();
90       m_slice_data = m_array->jit_slice_data () - 1;
91       m_slice_len = m_array->numel ();
92       m_dimensions = m_array->jit_dimensions ();
93     }
94 
updatejit_array95     void update (T *aarray)
96     {
97       m_array = aarray;
98       update ();
99     }
100 
Tjit_array101     operator T () const
102     {
103       return *m_array;
104     }
105 
106     int m_ref_count;
107 
108     U *m_slice_data;
109     octave_idx_type m_slice_len;
110     octave_idx_type *m_dimensions;
111 
112     T *m_array;
113   };
114 
115   typedef jit_array<NDArray, double> jit_matrix;
116 
117   std::ostream& operator << (std::ostream& os, const jit_matrix& mat);
118 
119   // calling convention
120   namespace jit_convention
121   {
122     enum
123     type
124     {
125       // internal to jit
126       internal,
127 
128       // an external C call
129       external,
130 
131       length
132     };
133   }
134 
135   // Used to keep track of estimated (infered) types during JIT.  This is a
136   // hierarchical type system which includes both concrete and abstract types.
137   //
138   // The types form a lattice.  Currently we only allow for one parent type, but
139   // eventually we may allow for multiple predecessors.
140   class
141   jit_type
142   {
143   public:
144 
145     typedef llvm::Value *(*convert_fn) (llvm::IRBuilderD&, llvm::Value *);
146 
147     jit_type (const std::string& aname, jit_type *aparent, llvm::Type *allvm_type,
148               bool askip_paren, int aid);
149 
150     // a user readable type name
name(void)151     const std::string& name (void) const { return m_name; }
152 
153     // a unique id for the type
type_id(void)154     int type_id (void) const { return m_id; }
155 
156     // An abstract base type, may be null
parent(void)157     jit_type * parent (void) const { return m_parent; }
158 
159     // convert to an llvm type
to_llvm(void)160     llvm::Type * to_llvm (void) const { return m_llvm_type; }
161 
162     // how this type gets passed as a function argument
163     llvm::Type * to_llvm_arg (void) const;
164 
depth(void)165     std::size_t depth (void) const { return m_depth; }
166 
skip_paren(void)167     bool skip_paren (void) const { return m_skip_paren; }
168 
169     // -------------------- Calling Convention information --------------------
170 
171     // A function declared like: mytype foo (int arg0, int arg1);
172     // Will be converted to: void foo (mytype *retval, int arg0, int arg1)
173     // if mytype is sret.  The caller is responsible for allocating space for
174     // retval. (on the stack)
sret(jit_convention::type cc)175     bool sret (jit_convention::type cc) const { return m_sret[cc]; }
176 
mark_sret(jit_convention::type cc)177     void mark_sret (jit_convention::type cc)
178     { m_sret[cc] = true; }
179 
180     // A function like: void foo (mytype arg0)
181     // Will be converted to: void foo (mytype *arg0)
182     // Basically just pass by reference.
pointer_arg(jit_convention::type cc)183     bool pointer_arg (jit_convention::type cc) const { return m_pointer_arg[cc]; }
184 
mark_pointer_arg(jit_convention::type cc)185     void mark_pointer_arg (jit_convention::type cc)
186     { m_pointer_arg[cc] = true; }
187 
188     // Convert into an equivalent form before calling.  For example, complex is
189     // represented as two values llvm vector, but we need to pass it as a two
190     // valued llvm structure to C functions.
pack(jit_convention::type cc)191     convert_fn pack (jit_convention::type cc) { return m_pack[cc]; }
192 
set_pack(jit_convention::type cc,convert_fn fn)193     void set_pack (jit_convention::type cc, convert_fn fn) { m_pack[cc] = fn; }
194 
195     // The inverse operation of pack.
unpack(jit_convention::type cc)196     convert_fn unpack (jit_convention::type cc) { return m_unpack[cc]; }
197 
set_unpack(jit_convention::type cc,convert_fn fn)198     void set_unpack (jit_convention::type cc, convert_fn fn)
199     { m_unpack[cc] = fn; }
200 
201     // The resulting type after pack is called.
packed_type(jit_convention::type cc)202     llvm::Type * packed_type (jit_convention::type cc)
203     { return m_packed_type[cc]; }
204 
set_packed_type(jit_convention::type cc,llvm::Type * ty)205     void set_packed_type (jit_convention::type cc, llvm::Type *ty)
206     { m_packed_type[cc] = ty; }
207 
208   private:
209 
210     std::string m_name;
211     jit_type *m_parent;
212     llvm::Type *m_llvm_type;
213     int m_id;
214     std::size_t m_depth;
215     bool m_skip_paren;
216 
217     bool m_sret[jit_convention::length];
218     bool m_pointer_arg[jit_convention::length];
219 
220     convert_fn m_pack[jit_convention::length];
221     convert_fn m_unpack[jit_convention::length];
222 
223     llvm::Type *m_packed_type[jit_convention::length];
224   };
225 
226   // separate print function to allow easy printing if type is null
227   std::ostream& jit_print (std::ostream& os, jit_type *atype);
228 
229   // Find common type
230   jit_type* jit_type_join (jit_type *lhs, jit_type *rhs);
231 
232 
233 
234   class jit_value;
235 
236   // An abstraction for calling llvm functions with jit_values.  Deals with
237   // calling convention details.
238   class
239   jit_function
240   {
241     friend std::ostream& operator << (std::ostream& os, const jit_function& fn);
242 
243   public:
244 
245     // create a function in an invalid state
246     jit_function (void);
247 
248     jit_function (const jit_module *amodule, jit_convention::type acall_conv,
249                   const llvm::Twine& aname, jit_type *aresult,
250                   const std::vector<jit_type *>& aargs);
251 
252     // Use an existing function, but change the argument types.  The new argument
253     // types must behave the same for the current calling convention.
254     jit_function (const jit_function& fn, jit_type *aresult,
255                   const std::vector<jit_type *>& aargs);
256 
257     jit_function (const jit_function& fn);
258 
259     // erase the internal LLVM function (if it exists).  Will become invalid.
260     void erase (void);
261 
valid(void)262     bool valid (void) const { return m_llvm_function; }
263 
264     std::string name (void) const;
265 
266     llvm::BasicBlock * new_block (const std::string& aname = "body",
267                                   llvm::BasicBlock *insert_before = nullptr);
268 
269     typedef std::vector<llvm::Value *> arg_vec;
270 
271     llvm::Value * call (llvm::IRBuilderD& builder,
272                         const arg_vec& in_args = arg_vec ()) const;
273 
274     llvm::Value * call (llvm::IRBuilderD& builder,
275                         const std::vector<jit_value *>& in_args) const;
276 
277     template <typename ...Args>
call(llvm::IRBuilderD & builder,arg_vec & in_args,llvm::Value * arg1,Args...other_args)278     llvm::Value * call (llvm::IRBuilderD& builder, arg_vec& in_args,
279                         llvm::Value * arg1, Args... other_args) const
280     {
281       in_args.push_back (arg1);
282       return call (builder, in_args, other_args...);
283     }
284 
285     template <typename T, typename ...Args>
call(llvm::IRBuilderD & builder,arg_vec & in_args,T * arg1,Args...other_args)286     llvm::Value * call (llvm::IRBuilderD& builder, arg_vec& in_args,
287                         T * arg1, Args... other_args) const
288     {
289       in_args.push_back (arg1->to_llvm ());
290       return call (builder, in_args, other_args...);
291     }
292 
293     template <typename ...Args>
call(llvm::IRBuilderD & builder,llvm::Value * arg1,Args...other_args)294     llvm::Value * call (llvm::IRBuilderD& builder, llvm::Value * arg1,
295                         Args... other_args) const
296     {
297       arg_vec in_args;
298       in_args.reserve (1 + sizeof... (other_args));
299       in_args.push_back (arg1);
300       return call (builder, in_args, other_args...);
301     }
302 
303     template <typename T, typename ...Args>
call(llvm::IRBuilderD & builder,T * arg1,Args...other_args)304     llvm::Value * call (llvm::IRBuilderD& builder, T * arg1,
305                         Args... other_args) const
306     {
307       arg_vec in_args;
308       in_args.reserve (1 + sizeof... (other_args));
309       in_args.push_back (arg1->to_llvm ());
310       return call (builder, in_args, other_args...);
311     }
312 
313     llvm::Value * argument (llvm::IRBuilderD& builder, std::size_t idx) const;
314 
315     void do_return (llvm::IRBuilderD& builder, llvm::Value *rval = nullptr,
316                     bool verify = true);
317 
to_llvm(void)318     llvm::Function * to_llvm (void) const { return m_llvm_function; }
319 
320     // If true, then the return value is passed as a pointer in the first argument
sret(void)321     bool sret (void) const { return m_result && m_result->sret (m_call_conv); }
322 
can_error(void)323     bool can_error (void) const { return m_can_error; }
324 
mark_can_error(void)325     void mark_can_error (void) { m_can_error = true; }
326 
result(void)327     jit_type * result (void) const { return m_result; }
328 
argument_type(std::size_t idx)329     jit_type * argument_type (std::size_t idx) const
330     {
331       assert (idx < m_args.size ());
332       return m_args[idx];
333     }
334 
arguments(void)335     const std::vector<jit_type *>& arguments (void) const { return m_args; }
336 
337   private:
338 
339     const jit_module *m_module;
340     llvm::Function *m_llvm_function;
341     jit_type *m_result;
342     std::vector<jit_type *> m_args;
343     jit_convention::type m_call_conv;
344     bool m_can_error;
345   };
346 
347   std::ostream& operator << (std::ostream& os, const jit_function& fn);
348 
349   // Keeps track of information about how to implement operations (+, -,
350   // *, etc.) and their resulting types.
351   class
352   jit_operation
353   {
354   public:
355 
jit_operation(const std::string & aname)356     jit_operation (const std::string& aname)  { m_name = aname; }
357 
358     // type signature vector
359     typedef std::vector<jit_type *> signature_vec;
360 
361     virtual ~jit_operation (void);
362 
add_overload(const jit_function & func)363     void add_overload (const jit_function& func)
364     {
365       add_overload (func, func.arguments ());
366     }
367 
368     void add_overload (const jit_function& func,
369                        const signature_vec& args);
370 
371     const jit_function& overload (const signature_vec& types) const;
372 
373     template <typename ...Args>
overload(signature_vec & args,jit_type * arg1,Args...other_args)374     const jit_function& overload (signature_vec& args, jit_type * arg1,
375                                   Args... other_args) const
376     {
377       args.push_back (arg1);
378       return overload (args, other_args...);
379     }
380 
381     template <typename ...Args>
overload(jit_type * arg1,Args...other_args)382     const jit_function& overload (jit_type * arg1, Args... other_args) const
383     {
384       signature_vec args;
385       args.reserve (1 + sizeof... (other_args));
386       args.push_back (arg1);
387       return overload (args, other_args...);
388     }
389 
result(const signature_vec & types)390     jit_type * result (const signature_vec& types) const
391     {
392       const jit_function& temp = overload (types);
393       return temp.result ();
394     }
395 
396     template <typename ...Args>
result(signature_vec & args,jit_type * arg1,Args...other_args)397     jit_type * result (signature_vec& args, jit_type * arg1,
398                        Args... other_args) const
399     {
400       args.push_back (arg1);
401       return overload (args, other_args...);
402     }
403 
404     template <typename ...Args>
result(jit_type * arg1,Args...other_args)405     jit_type * result (jit_type * arg1, Args... other_args) const
406     {
407       signature_vec args;
408       args.reserve (1 + sizeof... (other_args));
409       args.push_back (arg1);
410       return overload (args, other_args...);
411     }
412 
name(void)413     const std::string& name (void) const { return m_name; }
414 
stash_name(const std::string & aname)415     void stash_name (const std::string& aname) { m_name = aname; }
416 
417   protected:
418 
419     virtual jit_function * generate (const signature_vec& types) const;
420 
421   private:
422 
423     Array<octave_idx_type> to_idx (const signature_vec& types) const;
424 
425     const jit_function& do_generate (const signature_vec& types) const;
426 
427     struct signature_cmp
428     {
429       bool operator () (const signature_vec *lhs, const signature_vec *rhs) const;
430     };
431 
432     typedef std::map<const signature_vec *, jit_function *, signature_cmp>
433       generated_map;
434 
435     mutable generated_map m_generated;
436 
437     std::vector<Array<jit_function>> m_overloads;
438 
439     std::string m_name;
440   };
441 
442 
443   class
444   jit_index_operation : public jit_operation
445   {
446   public:
447 
jit_index_operation(const jit_typeinfo & ti,const std::string & name)448     jit_index_operation (const jit_typeinfo& ti, const std::string& name)
449       : jit_operation (name), m_typeinfo (ti) { }
450 
451   protected:
452 
453     virtual jit_function * generate (const signature_vec& types) const;
454 
455     virtual jit_function * generate_matrix (const signature_vec& types) const = 0;
456 
457     // helper functions
458     // [start_idx, end_idx).
459     llvm::Value * create_arg_array (llvm::IRBuilderD& builder,
460                                     const jit_function& fn, std::size_t start_idx,
461                                     std::size_t end_idx) const;
462 
463     const jit_typeinfo& m_typeinfo;
464   };
465 
466   class
467   jit_paren_subsref : public jit_index_operation
468   {
469   public:
470 
471     // FIXME: Avoid creating object in an invalid state?
472     jit_paren_subsref (const jit_typeinfo& ti);
473     ~jit_paren_subsref (void);
474     void init_paren_scalar (void);
475 
476   protected:
477 
478     virtual jit_function * generate_matrix (const signature_vec& types) const;
479 
480   private:
481 
482     jit_function *m_paren_scalar;
483   };
484 
485   class
486   jit_paren_subsasgn : public jit_index_operation
487   {
488   public:
489 
490     // FIXME: Avoid creating object in an invalid state?
491     jit_paren_subsasgn (const jit_typeinfo& ti);
492     ~jit_paren_subsasgn (void);
493     void init_paren_scalar (void);
494 
495   protected:
496 
497     jit_function * generate_matrix (const signature_vec& types) const;
498 
499   private:
500 
501     jit_function *m_paren_scalar;
502   };
503 
504 
505   // A singleton class which handles the construction of jit_types
506   class
507   jit_typeinfo
508   {
509     // ----- Constructor/destructor (singleton pattern) -----
510 
511   public:
512 
513     ~jit_typeinfo (void);
514 
515   private:
516 
517     static jit_typeinfo& instance (void);
518     jit_typeinfo (void);
519     static bool s_in_construction;
520 
521     // ----- Registering types -----
522 
523   public:
524 
525     static jit_type *register_new_type (const std::string& name, jit_type *parent,
526                                         llvm::Type *llvm_type, bool skip_paren = false)
527     {
528       return instance ().do_register_new_type (name, parent, llvm_type, skip_paren);
529     }
530 
531   private:
532 
533     // List of all registered types
534     std::vector<jit_type*> m_id_to_type;
535 
536     // Register a new type
537     jit_type *do_register_new_type (const std::string& name, jit_type *parent,
538                                     llvm::Type *llvm_type, bool skip_paren = false);
539 
540     // ----- Base types -----
541 
542   public:
543 
get_any(void)544     static jit_type *get_any (void) { return instance ().m_any; }
545 
get_matrix(void)546     static jit_type *get_matrix (void) { return instance ().m_matrix; }
547 
get_scalar(void)548     static jit_type *get_scalar (void) { return instance ().m_scalar; }
549 
get_scalar_ptr(void)550     static jit_type *get_scalar_ptr (void) { return instance ().m_scalar_ptr; }
551 
get_any_ptr(void)552     static jit_type *get_any_ptr (void) { return instance ().m_any_ptr; }
553 
get_range(void)554     static jit_type *get_range (void) { return instance ().m_range; }
555 
get_string(void)556     static jit_type *get_string (void) { return instance ().m_string; }
557 
get_bool(void)558     static jit_type *get_bool (void) { return instance ().m_boolean; }
559 
get_index(void)560     static jit_type *get_index (void) { return instance ().m_index; }
561 
get_complex(void)562     static jit_type *get_complex (void) { return instance ().m_complex; }
563 
intN(std::size_t nbits)564     static jit_type *intN (std::size_t nbits) { return instance ().do_get_intN (nbits); }
565 
566     // FIXME: do we really need these two ?
get_scalar_llvm(void)567     static llvm::Type *get_scalar_llvm (void) { return instance ().m_scalar->to_llvm (); }  // this one is weird
568 
get_index_llvm(void)569     static llvm::Type *get_index_llvm (void)  { return instance ().m_index->to_llvm (); }  // this one is weird too
570 
571   private:
572 
573     // Base types as LLVM types
574 
575     llvm::Type *m_any_t;
576     llvm::Type *m_bool_t;  // FIXME: should be "boolean_t", for consistency
577     llvm::Type *m_complex_t;
578     llvm::Type *m_index_t;
579     llvm::Type *m_scalar_t;
580     llvm::Type *m_string_t;
581 
582     llvm::StructType *m_range_t;
583     llvm::StructType *m_matrix_t;
584 
585     // Base types as jit_type objects)
586 
587     jit_type *m_any;
588     jit_type *m_boolean;
589     jit_type *m_complex;
590     jit_type *m_index;
591     jit_type *m_scalar;
592     jit_type *m_string;
593 
594     jit_type *m_range;
595     jit_type *m_matrix;
596 
597     jit_type *m_scalar_ptr;  // a fake type for interfacing with C++
598     jit_type *m_any_ptr;     // a fake type for interfacing with C++ (bis)
599     jit_type *m_unknown_function;
600 
601     // complex_ret is what is passed to C functions
602     // in order to get calling convention right
603     llvm::StructType *m_complex_ret;
604 
605     // Get integer type from number of bits
606     jit_type *do_get_intN (std::size_t nbits) const;
607 
608     // map container for integer types: int8, int16, etc.
609     // (note that they are also stored in id_to_types)
610     std::map<std::size_t, jit_type *> m_ints;
611 
612     // ----- parenthesis subsref/subsasgn -----
613 
614     friend jit_paren_subsref;
615     friend jit_paren_subsasgn;
616 
617   public:
618 
paren_subsref(void)619     static const jit_operation& paren_subsref (void)   { return instance ().paren_subsref_fn; }
paren_subsasgn(void)620     static const jit_operation& paren_subsasgn (void)  { return instance ().paren_subsasgn_fn; }
621 
622   private:
623 
624     jit_paren_subsref paren_subsref_fn;
625     jit_paren_subsasgn paren_subsasgn_fn;
626 
627     // ----- Miscellaneous (FIXME: needs to be organized) -----
628 
629   public:
630 
631     // Get the jit_type of an octave_value
type_of(const octave_value & ov)632     static jit_type *type_of (const octave_value &ov)
633     {
634       return instance ().do_type_of (ov);
635     };
636 
637     // Get a unary or binary operation from its integer id
binary_op(int op)638     static const jit_operation& binary_op (int op)
639     {
640       return instance ().do_binary_op (op);
641     }
642 
unary_op(int op)643     static const jit_operation& unary_op (int op)
644     {
645       return instance ().do_unary_op (op);
646     }
647 
grab(void)648     static const jit_operation& grab (void)
649     {
650       return instance ().m_grab_fn;
651     }
652 
get_grab(jit_type * type)653     static const jit_function& get_grab (jit_type *type)
654     {
655       return instance ().m_grab_fn.overload (type);
656     }
657 
release(void)658     static const jit_operation& release (void)
659     {
660       return instance ().m_release_fn;
661     }
662 
get_release(jit_type * type)663     static const jit_function& get_release (jit_type *type)
664     {
665       return instance ().m_release_fn.overload (type);
666     }
667 
destroy(void)668     static const jit_operation& destroy (void)
669     {
670       return instance ().m_destroy_fn;
671     }
672 
print_value(void)673     static const jit_operation& print_value (void)
674     {
675       return instance ().m_print_fn;
676     }
677 
for_init(void)678     static const jit_operation& for_init (void)
679     {
680       return instance ().m_for_init_fn;
681     }
682 
for_check(void)683     static const jit_operation& for_check (void)
684     {
685       return instance ().m_for_check_fn;
686     }
687 
for_index(void)688     static const jit_operation& for_index (void)
689     {
690       return instance ().m_for_index_fn;
691     }
692 
make_range(void)693     static const jit_operation& make_range (void)
694     {
695       return instance ().m_make_range_fn;
696     }
697 
logically_true(void)698     static const jit_operation& logically_true (void)
699     {
700       return instance ().m_logically_true_fn;
701     }
702 
cast(jit_type * result)703     static const jit_operation& cast (jit_type *result)
704     {
705       return instance ().do_cast (result);
706     }
707 
cast(jit_type * to,jit_type * from)708     static const jit_function& cast (jit_type *to, jit_type *from)
709     {
710       return instance ().do_cast (to, from);
711     }
712 
insert_error_check(llvm::IRBuilderD & bld)713     static llvm::Value *insert_error_check (llvm::IRBuilderD& bld)
714     {
715       return instance ().do_insert_error_check (bld);
716     }
717 
insert_interrupt_check(llvm::IRBuilderD & bld)718     static llvm::Value *insert_interrupt_check (llvm::IRBuilderD& bld)
719     {
720       return instance ().do_insert_interrupt_check (bld);
721     }
722 
end(void)723     static const jit_operation& end (void)
724     {
725       return instance ().m_end_fn;
726     }
727 
end(jit_value * value,jit_value * idx,jit_value * count)728     static const jit_function& end (jit_value *value, jit_value *idx,
729                                     jit_value *count)
730     {
731       return instance ().do_end (value, idx, count);
732     }
733 
create_undef(void)734     static const jit_operation& create_undef (void)
735     {
736       return instance ().m_create_undef_fn;
737     }
738 
create_complex(llvm::Value * real,llvm::Value * imag)739     static llvm::Value *create_complex (llvm::Value *real, llvm::Value *imag)
740     {
741       return instance ().complex_new (real, imag);
742     }
743 
pack_complex(llvm::IRBuilderD & bld,llvm::Value * cplx)744     static llvm::Value *pack_complex (llvm::IRBuilderD& bld, llvm::Value *cplx)
745     {
746       return instance ().do_pack_complex (bld, cplx);
747     }
748 
749     static llvm::Value *unpack_complex (llvm::IRBuilderD& bld,
750                                         llvm::Value *result);
751 
752   private:
753 
754     jit_type * do_type_of (const octave_value& ov) const;
755 
do_binary_op(int op)756     const jit_operation& do_binary_op (int op) const
757     {
758       assert (static_cast<std::size_t> (op) < m_binary_ops.size ());
759       return m_binary_ops[op];
760     }
761 
do_unary_op(int op)762     const jit_operation& do_unary_op (int op) const
763     {
764       assert (static_cast<std::size_t> (op) < m_unary_ops.size ());
765       return m_unary_ops[op];
766     }
767 
do_cast(jit_type * to)768     const jit_operation& do_cast (jit_type *to)
769     {
770       static jit_operation null_function ("null_function");
771       if (! to)
772         return null_function;
773 
774       std::size_t id = to->type_id ();
775       if (id >= m_casts.size ())
776         return null_function;
777       return m_casts[id];
778     }
779 
do_cast(jit_type * to,jit_type * from)780     const jit_function& do_cast (jit_type *to, jit_type *from)
781     {
782       return do_cast (to).overload (from);
783     }
784 
785     const jit_function& do_end (jit_value *value, jit_value *index,
786                                 jit_value *count);
787 
788     void add_print (jit_type *ty, void *fptr);
789 
790     void add_binary_op (jit_type *ty, int op, int llvm_op);
791 
792     void add_binary_icmp (jit_type *ty, int op, int llvm_op);
793 
794     void add_binary_fcmp (jit_type *ty, int op, int llvm_op);
795 
796     // type signature vector
797     typedef std::vector<jit_type *> signature_vec;
798 
799     // create a function with an external calling convention
800     // forces the function pointer to be specified
801     template <typename T>
802     jit_function create_external (T fn, const llvm::Twine& name,
803                                   jit_type * ret, const signature_vec& args
804                                   = signature_vec ()) const;
805 
806     template <typename T, typename ...Args>
create_external(T fn,const llvm::Twine & name,jit_type * ret,signature_vec & args,jit_type * arg1,Args...other_args)807     jit_function create_external (T fn, const llvm::Twine& name,
808                                   jit_type * ret, signature_vec& args,
809                                   jit_type * arg1, Args... other_args) const
810     {
811       args.push_back (arg1);
812       return create_external (fn, name, ret, args, other_args...);
813     }
814 
815     template <typename T, typename ...Args>
create_external(T fn,const llvm::Twine & name,jit_type * ret,jit_type * arg1,Args...other_args)816     jit_function create_external (T fn, const llvm::Twine& name, jit_type *ret,
817                                   jit_type * arg1, Args... other_args) const
818     {
819       signature_vec args;
820       args.reserve (1 + sizeof... (other_args));
821       args.push_back (arg1);
822       return create_external (fn, name, ret, args, other_args...);
823     }
824 
825     // create an internal calling convention (a function defined in llvm)
826     jit_function create_internal (const llvm::Twine& name, jit_type *ret,
827                                   const signature_vec& args
828                                   = signature_vec ()) const
829     {
830       return jit_function (m_base_jit_module, jit_convention::internal,
831                            name, ret, args);
832     }
833 
834     template <typename ...Args>
create_internal(const llvm::Twine & name,jit_type * ret,signature_vec & args,jit_type * arg1,Args...other_args)835     jit_function create_internal (const llvm::Twine& name, jit_type *ret,
836                                   signature_vec& args,
837                                   jit_type * arg1, Args... other_args) const
838     {
839       args.push_back (arg1);
840       return create_internal (name, ret, args, other_args...);
841     }
842 
843     template <typename ...Args>
create_internal(const llvm::Twine & name,jit_type * ret,jit_type * arg1,Args...other_args)844     jit_function create_internal (const llvm::Twine& name, jit_type *ret,
845                                   jit_type * arg1, Args... other_args) const
846     {
847       signature_vec args;
848       args.reserve (1 + sizeof... (other_args));
849       args.push_back (arg1);
850       return create_internal (name, ret, args, other_args...);
851     }
852 
853     jit_function create_identity (jit_type *type);
854 
855     llvm::Value * do_insert_error_check (llvm::IRBuilderD& bld);
856 
857     llvm::Value * do_insert_interrupt_check (llvm::IRBuilderD& bld);
858 
859     void add_builtin (const std::string& name);
860 
register_intrinsic(const std::string & name,std::size_t id,jit_type * result,jit_type * arg0)861     void register_intrinsic (const std::string& name, std::size_t id,
862                              jit_type *result, jit_type *arg0)
863     {
864       std::vector<jit_type *> args (1, arg0);
865       register_intrinsic (name, id, result, args);
866     }
867 
868     void register_intrinsic (const std::string& name, std::size_t id, jit_type *result,
869                              const std::vector<jit_type *>& args);
870 
register_generic(const std::string & name,jit_type * result,jit_type * arg0)871     void register_generic (const std::string& name, jit_type *result,
872                            jit_type *arg0)
873     {
874       std::vector<jit_type *> args (1, arg0);
875       register_generic (name, result, args);
876     }
877 
878     void register_generic (const std::string& name, jit_type *result,
879                            const std::vector<jit_type *>& args);
880 
881     octave_builtin * find_builtin (const std::string& name);
882 
883     jit_function mirror_binary (const jit_function& fn);
884 
885     llvm::Function * wrap_complex (llvm::Function *wrap);
886 
887     llvm::Value * complex_real (llvm::Value *cx);
888 
889     llvm::Value * complex_real (llvm::Value *cx, llvm::Value *real);
890 
891     llvm::Value * complex_imag (llvm::Value *cx);
892 
893     llvm::Value * complex_imag (llvm::Value *cx, llvm::Value *imag);
894 
895     llvm::Value * complex_new (llvm::Value *real, llvm::Value *imag);
896 
897     llvm::Value *do_pack_complex (llvm::IRBuilderD& bld, llvm::Value *cplx) const;
898 
899     int m_next_id;
900 
901     llvm::GlobalVariable *m_lerror_state;
902     llvm::GlobalVariable *m_loctave_interrupt_state;
903 
904     llvm::Type *m_sig_atomic_type;
905 
906     std::map<std::string, jit_type *> m_builtins;
907 
908     std::vector<jit_operation> m_binary_ops;
909     std::vector<jit_operation> m_unary_ops;
910     jit_operation m_grab_fn;
911     jit_operation m_release_fn;
912     jit_operation m_destroy_fn;
913     jit_operation m_print_fn;
914     jit_operation m_for_init_fn;
915     jit_operation m_for_check_fn;
916     jit_operation m_for_index_fn;
917     jit_operation m_logically_true_fn;
918     jit_operation m_make_range_fn;
919     jit_operation m_end1_fn;
920     jit_operation m_end_fn;
921     jit_operation m_create_undef_fn;
922 
923     jit_function m_any_call;
924 
925     // type id -> cast function TO that type
926     std::vector<jit_operation> m_casts;
927 
928     // type id -> identity function
929     std::vector<jit_function> m_identities;
930 
931     jit_module *m_base_jit_module;
932 
933     llvm::IRBuilderD *m_builder_ptr;
934     llvm::IRBuilderD& m_builder;
935   };
936 }
937 
938 #endif
939 #endif
940