Changes in / [f20dffa:6013bd7]
- Files:
-
- 2 deleted
- 12 edited
-
doc/generic_types/.gitignore (modified) (1 diff)
-
doc/generic_types/generic_types.bib (modified) (1 diff)
-
doc/generic_types/generic_types.tex (modified) (9 diffs)
-
doc/rob_thesis/.gitignore (deleted)
-
src/CodeGen/CodeGenerator.cc (modified) (1 diff)
-
src/libcfa/Makefile.am (modified) (1 diff)
-
src/libcfa/Makefile.in (modified) (1 diff)
-
src/tests/.expect/32/KRfunctions.txt (modified) (3 diffs)
-
src/tests/.expect/32/attributes.txt (deleted)
-
src/tests/.expect/64/KRfunctions.txt (modified) (3 diffs)
-
src/tests/.expect/64/attributes.txt (modified) (4 diffs)
-
src/tests/Makefile.am (modified) (1 diff)
-
src/tests/Makefile.in (modified) (1 diff)
-
src/tests/test.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
doc/generic_types/.gitignore
rf20dffa r6013bd7 13 13 *.ps 14 14 *.toc 15 *.lof16 *.lot17 15 *.synctex.gz -
doc/generic_types/generic_types.bib
rf20dffa r6013bd7 20 20 note = {\href{http://plg.uwaterloo.ca/theses/DitchfieldThesis.pdf}{http://\-plg.uwaterloo.ca/\-theses/\-DitchfieldThesis.pdf}} 21 21 } 22 23 @mastersthesis{Schluntz17,24 author = {Schluntz, Robert},25 title = {Resource Management and Tuples in C$\mathbf{\forall}$},26 school = {School of Computer Science, University of Waterloo},27 year = 2017,28 address = {Waterloo, Ontario, Canada, N2L 3G1},29 note = {[[unpublished]]}30 } -
doc/generic_types/generic_types.tex
rf20dffa r6013bd7 20 20 morekeywords={_Alignas,_Alignof,__alignof,__alignof__,asm,__asm,__asm__,_At,_Atomic,__attribute,__attribute__,auto, 21 21 _Bool,catch,catchResume,choose,_Complex,__complex,__complex__,__const,__const__,disable,dtype,enable,__extension__, 22 fallthrough,fallthru,finally,forall,ftype,_Generic,_Imaginary,inline,__label__,lvalue,_Noreturn,one_t,otype,restrict, sized,_Static_assert,22 fallthrough,fallthru,finally,forall,ftype,_Generic,_Imaginary,inline,__label__,lvalue,_Noreturn,one_t,otype,restrict,_Static_assert, 23 23 _Thread_local,throw,throwResume,trait,try,typeof,__typeof,__typeof__,zero_t}, 24 24 }% … … 57 57 \acmJournal{PACMPL} 58 58 59 \title{Generic and TupleTypes with Efficient Dynamic Layout in \CFA{}}59 \title{Generic Types with Efficient Dynamic Layout in \CFA{}} 60 60 61 61 \author{Aaron Moss} … … 95 95 \email{pabuhr@uwaterloo.ca} 96 96 97 \terms{generic, t uple, types}98 \keywords{generic types, tuple types, polymorphic functions, C, Cforall}97 \terms{generic, types} 98 \keywords{generic types, polymorphic functions, Cforall} 99 99 100 100 \begin{CCSXML} … … 132 132 \section{Introduction \& Background} 133 133 134 \CFA{}\footnote{Pronounced ``C-for-all'', and written \CFA{} or Cforall.} is an evolutionary extension of the C programming language which aims to add modern language features to C while maintaining both source compatibility with C and a familiar mental model for programmers. Four key design goals were set out in the original design of \CFA{} \citep{Bilson03}: 135 \begin{enumerate} 136 \item The behaviour of standard C code must remain the same when translated by a \CFA{} compiler as when translated by a C compiler. 137 \item Standard C code must be as fast and as small when translated by a \CFA{} compiler as when translated by a C compiler. 138 \item \CFA{} code must be at least as portable as standard C code. 139 \item Extensions introduced by \CFA{} must be translated in the most efficient way possible. 140 \end{enumerate} 141 The purpose of these goals is to ensure that existing C codebases can be converted to \CFA{} incrementally and with minimal effort, and that programmers who already know C can productively produce \CFA{} code without extensive training beyond the extension features they wish to employ. 142 143 \CFA{} has been previously extended with polymorphic functions and name overloading (including operator overloading) \citep{Bilson03}, and deterministically-executed constructors and destructors \citep{Schluntz17}. This paper describes how generic and tuple types are designed and implemented in \CFA{} in accordance with both the backward compatibility goals and existing features described above. 134 \CFA{}\footnote{Pronounced ``C-for-all'', and written \CFA{} or Cforall.} is an evolutionary extension of the C programming language which aims to add modern language features to C while maintaining both source compatibility with C and a familiar mental model for programmers. This paper describes how generic types are designed and implemented in \CFA{}, and how they interact with \CFA{}'s polymorphic functions. 144 135 145 136 \subsection{Polymorphic Functions} 146 \label{sec:poly-fns}147 137 148 138 \CFA{}'s polymorphism was originally formalized by \citet{Ditchfield92}, and first implemented by \citet{Bilson03}. The signature feature of \CFA{} is parametric-polymorphic functions; such functions are written using a @forall@ clause (which gives the language its name): … … 201 191 } 202 192 \end{lstlisting} 203 This capability allows specifying the same set of assertions in multiple locations, without the repetition and likelihood of mistakes that come with manually writing them out for each function declaration.204 193 205 194 @otype@ is essentially syntactic sugar for the following trait: … … 209 198 void ?{}(T*); // default constructor 210 199 void ?{}(T*, T); // copy constructor 211 void?=?(T*, T); // assignment operator200 T ?=?(T*, T); // assignment operator 212 201 void ^?{}(T*); // destructor 213 202 }; 214 \end{lstlisting}215 Given this information, variables of polymorphic type can be treated as if they were a complete struct type -- they can be stack-allocated using the @alloca@ compiler builtin, default or copy-initialized, assigned, and deleted. As an example, the @abs@ function above would produce generated code something like the following (simplified for clarity and brevity):216 \begin{lstlisting}217 void abs( size_t _sizeof_M, size_t _alignof_M,218 void (*_ctor_M)(void*), void (*_copy_M)(void*, void*),219 void (*_assign_M)(void*, void*), void (*_dtor_M)(void*),220 bool (*_lt_M)(void*, void*), void (*_neg_M)(void*, void*),221 void (*_ctor_M_zero)(void*, int),222 void* m, void* _rtn ) { // polymorphic parameter and return passed as void*223 // M zero = { 0 };224 void* zero = alloca(_sizeof_M); // stack allocate 0 temporary225 _ctor_M_zero(zero, 0); // initialize using zero_t constructor226 // return m < zero ? -m : m;227 void *_tmp = alloca(_sizeof_M)228 _copy_M( _rtn, // copy-initialize return value229 _lt_M( m, zero ) ? // check condition230 (_neg_M(m, _tmp), _tmp) : // negate m231 m);232 _dtor_M(_tmp); _dtor_M(zero); // destroy temporaries233 }234 203 \end{lstlisting} 235 204 … … 313 282 \end{lstlisting} 314 283 315 Though \CFA{} implements concrete generic types efficiently, it also has a fully-general system for computing with dynamic generic types. As mentioned in Section~\ref{sec:poly-fns}, @otype@ function parameters (in fact all @sized@ polymorphic parameters) come with implicit size and alignment parameters provided by the caller. Dynamic generic structs also come with an \emph{offset array} which contains the offsets of each member of the struct\footnote{Dynamic generic unions need no such offset array, as all members are at offset 0.}. Access to members\footnote{The \lstinline@offsetof@ macro is implmented similarly.} of a dynamic generic struct is provided by adding the corresponding member of the offset array to the struct pointer at runtime, essentially moving a compile-time offset calculation to runtime where neccessary. 316 317 \TODO{} Discuss caller-provided versus callee-provided offset arrays, layout functions. 318 319 Whether a type is concrete, dtype-static, or dynamic is decided based solely on the type parameters and @forall@ clause on the struct declaration. This allows opaque forward declarations of generic types like @forall(otype T) struct Box;@ -- like in C, all uses of @Box(T)@ can be in a separately compiled translation unit, and callers from other translation units will know the proper calling conventions to use. If the definition of a struct type was included in the determination of dynamic or concrete, some further types may be recognized as dtype-static (\eg, @forall(otype T) struct unique_ptr { T* p };@ does not depend on @T@ for its layout, but the existence of an @otype@ parameter means that it \emph{could}.), but preserving separate compilation (and the associated C compatibility) in this way is judged to be an appropriate trade-off. 320 321 The re-use of dtype-static struct instantiations enables some useful programming patterns at zero runtime cost. The most important such pattern is using @forall(dtype T) T*@ as a type-checked replacement for @void*@, as in this example, which takes a @qsort@ or @bsearch@-compatible comparison routine and creates a similar lexicographic comparison for pairs of pointers: 284 \TODO{} Maybe move this after the rest of the discussion. 285 This re-use of dtype-static struct instantiations enables some useful programming patterns at zero runtime cost. The most important such pattern is using @forall(dtype T) T*@ as a type-checked replacement for @void*@, as in this example, which takes a @qsort@ or @bsearch@-compatible comparison routine and creates a similar lexicographic comparison for pairs of pointers: 322 286 \begin{lstlisting} 323 287 forall(dtype T) … … 347 311 scalar(metres) marathon = half_marathon + half_marathon; 348 312 scalar(litres) two_pools = swimming_pool + swimming_pool; 349 marathon + swimming_pool; // ERROR -- caught by compiler313 marathon + swimming_pool; // ERRORv -- caught by compiler 350 314 \end{lstlisting} 351 315 @scalar@ is a dtype-static type, so all uses of it will use a single struct definition, containing only a single @unsigned long@, and can share the same implementations of common routines like @?+?@ -- these implementations may even be separately compiled, unlike \CC{} template functions. However, the \CFA{} type-checker will ensure that matching types are used by all calls to @?+?@, preventing nonsensical computations like adding the length of a marathon to the volume of an olympic pool. … … 359 323 \section{Related Work} 360 324 361 \TODO{} Talk about \CC{}, Cyclone, maybe D, Rust, \etc{} 362 363 A major difference between the approaches of \CC{} and \CFA{} to polymorphism is that the set of assumed properties for a type is \emph{explicit} in \CFA{}. One of the major limiting factors of \CC{}'s approach is that templates cannot be separately compiled. In contrast, the explicit nature of assertions allows \CFA{}'s polymorphic functions to be separately compiled. 364 365 \section{Conclusion \& Future Work} 366 367 \TODO{} Among future work, talk about ideas for selectively template-expanding code (on decls for specific types, or on calls for accessible decls). 325 \TODO{} Talk about \CC{}, Cyclone, \etc{} 326 327 \section{Conclusion} 328 329 \TODO{} 368 330 369 331 \bibliographystyle{ACM-Reference-Format} -
src/CodeGen/CodeGenerator.cc
rf20dffa r6013bd7 147 147 148 148 void CodeGenerator::visit( ObjectDecl * objectDecl ) { 149 if (objectDecl->get_name().empty()) {150 static UniqueName name = { "__anonymous_object" };151 objectDecl->set_name( name.newName() );152 }153 154 149 extension( objectDecl ); 155 150 genAttributes( objectDecl->get_attributes() ); -
src/libcfa/Makefile.am
rf20dffa r6013bd7 35 35 ${AM_V_GEN}@BACKEND_CC@ @CFA_FLAGS@ -D__CFA_DEBUG__ -O0 -c -o $@ $< 36 36 37 EXTRA_FLAGS = -g -Wall -W error -Wno-unused-function -I${abs_top_srcdir}/src/libcfa/libhdr -imacros libcfa-prelude.c @CFA_FLAGS@37 EXTRA_FLAGS = -g -Wall -Wno-unused-function -I${abs_top_srcdir}/src/libcfa/libhdr -imacros libcfa-prelude.c @CFA_FLAGS@ 38 38 39 39 AM_CCASFLAGS = @CFA_FLAGS@ -
src/libcfa/Makefile.in
rf20dffa r6013bd7 305 305 AUTOMAKE_OPTIONS = subdir-objects 306 306 lib_LIBRARIES = $(am__append_1) $(am__append_2) 307 EXTRA_FLAGS = -g -Wall -W error -Wno-unused-function -I${abs_top_srcdir}/src/libcfa/libhdr -imacros libcfa-prelude.c @CFA_FLAGS@307 EXTRA_FLAGS = -g -Wall -Wno-unused-function -I${abs_top_srcdir}/src/libcfa/libhdr -imacros libcfa-prelude.c @CFA_FLAGS@ 308 308 AM_CCASFLAGS = @CFA_FLAGS@ 309 309 headers = limits stdlib math iostream fstream iterator rational assert \ -
src/tests/.expect/32/KRfunctions.txt
rf20dffa r6013bd7 47 47 int ___retval_f5__i_1; 48 48 } 49 int (*__f6__FPFi_i__iPiPi__1(int __a__i_1, int *__b__Pi_1, int *__c__Pi_1))(int __anonymous_object0){50 int (*___retval_f6__PFi_i__1)(int __anonymous_object1);49 int (*__f6__FPFi_i__iPiPi__1(int __a__i_1, int *__b__Pi_1, int *__c__Pi_1))(int ){ 50 int (*___retval_f6__PFi_i__1)(int ); 51 51 } 52 52 int (*__f7__FPFi_ii__iPiPi__1(int __a__i_1, int *__b__Pi_1, int *__c__Pi_1))(int __a__i_1, int __b__i_1){ … … 61 61 int *(*__f10__FPFPi_ii__iPiPid__1(int __a__i_1, int *__b__Pi_1, int *__c__Pi_1, double __y__d_1))(int __x__i_1, int __y__i_1){ 62 62 int *(*___retval_f10__PFPi_ii__1)(int __x__i_1, int __y__i_1); 63 int *__x__FPi_ii__2(int __anonymous_object2, int __anonymous_object3);63 int *__x__FPi_ii__2(int , int ); 64 64 ((void)(___retval_f10__PFPi_ii__1=__x__FPi_ii__2) /* ?{} */); 65 65 return ((int *(*)(int __x__i_1, int __y__i_1))___retval_f10__PFPi_ii__1); … … 79 79 const int __fred__FCi___1(){ 80 80 const int ___retval_fred__Ci_1; 81 int *(*__x__PFPi_ii__2)(int __anonymous_object4, int __anonymous_object5);81 int *(*__x__PFPi_ii__2)(int , int ); 82 82 int __a__i_2; 83 83 int __b__i_2; -
src/tests/.expect/64/KRfunctions.txt
rf20dffa r6013bd7 47 47 int ___retval_f5__i_1; 48 48 } 49 int (*__f6__FPFi_i__iPiPi__1(int __a__i_1, int *__b__Pi_1, int *__c__Pi_1))(int __anonymous_object0){50 int (*___retval_f6__PFi_i__1)(int __anonymous_object1);49 int (*__f6__FPFi_i__iPiPi__1(int __a__i_1, int *__b__Pi_1, int *__c__Pi_1))(int ){ 50 int (*___retval_f6__PFi_i__1)(int ); 51 51 } 52 52 int (*__f7__FPFi_ii__iPiPi__1(int __a__i_1, int *__b__Pi_1, int *__c__Pi_1))(int __a__i_1, int __b__i_1){ … … 61 61 int *(*__f10__FPFPi_ii__iPiPid__1(int __a__i_1, int *__b__Pi_1, int *__c__Pi_1, double __y__d_1))(int __x__i_1, int __y__i_1){ 62 62 int *(*___retval_f10__PFPi_ii__1)(int __x__i_1, int __y__i_1); 63 int *__x__FPi_ii__2(int __anonymous_object2, int __anonymous_object3);63 int *__x__FPi_ii__2(int , int ); 64 64 ((void)(___retval_f10__PFPi_ii__1=__x__FPi_ii__2) /* ?{} */); 65 65 return ((int *(*)(int __x__i_1, int __y__i_1))___retval_f10__PFPi_ii__1); … … 79 79 const int __fred__FCi___1(){ 80 80 const int ___retval_fred__Ci_1; 81 int *(*__x__PFPi_ii__2)(int __anonymous_object4, int __anonymous_object5);81 int *(*__x__PFPi_ii__2)(int , int ); 82 82 int __a__i_2; 83 83 int __b__i_2; -
src/tests/.expect/64/attributes.txt
rf20dffa r6013bd7 58 58 __attribute__ ((used,unused,unused)) int __f7__i_1; 59 59 __attribute__ ((used,used,unused)) int __f8__i_1; 60 __attribute__ ((unused)) int __anonymous_object0;60 __attribute__ ((unused)) int ; 61 61 __attribute__ ((unused,unused)) int *__f9__Pi_1; 62 62 }; … … 226 226 int **const ___retval_f2__CPPi_1; 227 227 } 228 __attribute__ ((unused,used,unused)) int (*__f3__FPA0i_i__1(int __anonymous_object1))[];228 __attribute__ ((unused,used,unused)) int (*__f3__FPA0i_i__1(int ))[]; 229 229 __attribute__ ((unused,unused)) int (*__f3__FPA0i_i__1(int __p__i_1))[]{ 230 230 int (*___retval_f3__PA0i_1)[]; 231 231 } 232 __attribute__ ((unused,used,unused)) int (*__f4__FPFi_i____1())(int __anonymous_object2);233 __attribute__ ((unused,unused)) int (*__f4__FPFi_i____1())(int __anonymous_object3){234 int (*___retval_f4__PFi_i__1)(int __anonymous_object4);232 __attribute__ ((unused,used,unused)) int (*__f4__FPFi_i____1())(int ); 233 __attribute__ ((unused,unused)) int (*__f4__FPFi_i____1())(int ){ 234 int (*___retval_f4__PFi_i__1)(int ); 235 235 } 236 236 __attribute__ ((__nothrow__,__leaf__,__malloc__)) extern void *malloc(long unsigned int __size); 237 237 __attribute__ ((__nothrow__,__leaf__)) extern void free(void *__ptr); 238 238 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void abort(void); 239 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern int atexit0(void (*__func)(void), void * __anonymous_object5, void *__anonymous_object6);239 __attribute__ ((__nothrow__,__leaf__,__nonnull__(1))) extern int atexit0(void (*__func)(void), void *, void *); 240 240 __attribute__ ((__nothrow__,__leaf__,__noreturn__)) extern void exit(int __status); 241 241 __attribute__ ((format(printf, 1, 2))) extern int printf(const char *__restrict __format, ...); … … 268 268 int __tpr2__Fi_PPi__1(__attribute__ ((unused,unused,unused,unused,unused,unused)) int **__Foo__PPi_1); 269 269 int __tpr3__Fi_Pi__1(__attribute__ ((unused,unused,unused)) int *__Foo__Pi_1); 270 int __tpr4__Fi_PFi_Pi___1(__attribute__ ((unused,unused)) int (* __anonymous_object7)(__attribute__ ((unused,unused)) int __anonymous_object8[((long unsigned int )5)]));270 int __tpr4__Fi_PFi_Pi___1(__attribute__ ((unused,unused)) int (*)(__attribute__ ((unused,unused)) int [((long unsigned int )5)])); 271 271 int __tpr5__Fi_PFi____1(__attribute__ ((unused,unused,unused)) int (*__Foo__PFi___1)()); 272 272 int __tpr6__Fi_PFi____1(__attribute__ ((unused,unused,unused)) int (*__Foo__PFi___1)()); 273 int __tpr7__Fi_PFi_PFi_i____1(__attribute__ ((unused,unused)) int (* __anonymous_object9)(__attribute__ ((unused)) int (*__anonymous_object10)(__attribute__ ((unused,unused)) int __anonymous_object11)));273 int __tpr7__Fi_PFi_PFi_i____1(__attribute__ ((unused,unused)) int (*)(__attribute__ ((unused)) int (*)(__attribute__ ((unused,unused)) int ))); 274 274 int __ad__Fi___1(){ 275 275 int ___retval_ad__i_1; … … 320 320 ((void)sizeof(enum __anonymous5 )); 321 321 } 322 int __apd1__Fi_PiPi__1(__attribute__ ((unused,unused,unused)) int * __anonymous_object12, __attribute__ ((unused,unused,unused)) int *__anonymous_object13);323 int __apd2__Fi_PPiPPi__1(__attribute__ ((unused,unused,unused,unused)) int ** __anonymous_object14, __attribute__ ((unused,unused,unused,unused)) int **__anonymous_object15);324 int __apd3__Fi_PiPi__1(__attribute__ ((unused,unused,unused)) int * __anonymous_object16, __attribute__ ((unused,unused,unused)) int *__anonymous_object17);325 int __apd4__Fi_PFi__PFi____1(__attribute__ ((unused,unused,unused)) int (* __anonymous_object18)(), __attribute__ ((unused,unused,unused)) int (*__anonymous_object19)());326 int __apd5__Fi_PFi_i_PFi_i___1(__attribute__ ((unused,unused,unused)) int (* __anonymous_object20)(__attribute__ ((unused)) int __anonymous_object21), __attribute__ ((unused,unused,unused)) int (*__anonymous_object22)(__attribute__ ((unused)) int __anonymous_object23));327 int __apd6__Fi_PFi__PFi____1(__attribute__ ((unused,unused,unused)) int (* __anonymous_object24)(), __attribute__ ((unused,unused,unused)) int (*__anonymous_object25)());328 int __apd7__Fi_PFi_i_PFi_i___1(__attribute__ ((unused,unused,unused)) int (* __anonymous_object26)(__attribute__ ((unused)) int __anonymous_object27), __attribute__ ((unused,unused,unused)) int (*__anonymous_object28)(__attribute__ ((unused)) int __anonymous_object29));322 int __apd1__Fi_PiPi__1(__attribute__ ((unused,unused,unused)) int *, __attribute__ ((unused,unused,unused)) int *); 323 int __apd2__Fi_PPiPPi__1(__attribute__ ((unused,unused,unused,unused)) int **, __attribute__ ((unused,unused,unused,unused)) int **); 324 int __apd3__Fi_PiPi__1(__attribute__ ((unused,unused,unused)) int *, __attribute__ ((unused,unused,unused)) int *); 325 int __apd4__Fi_PFi__PFi____1(__attribute__ ((unused,unused,unused)) int (*)(), __attribute__ ((unused,unused,unused)) int (*)()); 326 int __apd5__Fi_PFi_i_PFi_i___1(__attribute__ ((unused,unused,unused)) int (*)(__attribute__ ((unused)) int ), __attribute__ ((unused,unused,unused)) int (*)(__attribute__ ((unused)) int )); 327 int __apd6__Fi_PFi__PFi____1(__attribute__ ((unused,unused,unused)) int (*)(), __attribute__ ((unused,unused,unused)) int (*)()); 328 int __apd7__Fi_PFi_i_PFi_i___1(__attribute__ ((unused,unused,unused)) int (*)(__attribute__ ((unused)) int ), __attribute__ ((unused,unused,unused)) int (*)(__attribute__ ((unused)) int )); 329 329 struct Vad { 330 __attribute__ ((unused)) int __anonymous_object30;331 __attribute__ ((unused,unused)) int * __anonymous_object31;332 __attribute__ ((unused,unused)) int __anonymous_object32[((long unsigned int )10)];333 __attribute__ ((unused,unused)) int (* __anonymous_object33)();330 __attribute__ ((unused)) int ; 331 __attribute__ ((unused,unused)) int *; 332 __attribute__ ((unused,unused)) int [((long unsigned int )10)]; 333 __attribute__ ((unused,unused)) int (*)(); 334 334 }; 335 335 static inline void ___constructor__F_P4sVad_autogen___1(struct Vad *___dst__P4sVad_1); -
src/tests/Makefile.am
rf20dffa r6013bd7 51 51 @+python test.py --list --concurrent=${concurrent} 52 52 53 .dummy : .dummy.c54 ${CC} ${CFLAGS} -XCFA -n ${<} -o ${@}55 56 53 constant0-1DP : constant0-1.c 57 54 ${CC} ${CFLAGS} -DDUPS ${<} -o ${@} -
src/tests/Makefile.in
rf20dffa r6013bd7 669 669 @+python test.py --list --concurrent=${concurrent} 670 670 671 .dummy : .dummy.c672 ${CC} ${CFLAGS} -XCFA -n ${<} -o ${@}673 674 671 constant0-1DP : constant0-1.c 675 672 ${CC} ${CFLAGS} -DDUPS ${<} -o ${@} -
src/tests/test.py
rf20dffa r6013bd7 25 25 # parses the Makefile to find the machine type (32-bit / 64-bit) 26 26 def getMachineType(): 27 sh('echo " void ?{}(int*a,int b){}int main(){return 0;}" > .dummy.c')27 sh('echo "int main() { return 0; }" > .dummy.c') 28 28 sh("make .dummy", print2stdout=False) 29 29 _, out = sh("file .dummy", print2stdout=False)
Note:
See TracChangeset
for help on using the changeset viewer.