ROSE 0.11.145.192
OmpAttribute.h
1#ifndef Omp_Attribute_h_INCLUDED
2#define Omp_Attribute_h_INCLUDED
11//
12// Liao 9/17, 2008
13//
14
15#include <iostream>
16#include <string>
17#include <map>
18#include <cassert>
19#include <vector>
20class SgNode;
24namespace OmpSupport
25{
26 // OpenMP construct name list
27 //-------------------------------------------------------------------
28 // We put all directive and clause types into one enumerate type
29 // since some internal data structure(map) have to access
30 // both directives and clauses uniformly
31 enum omp_construct_enum
32 {
33 e_unknown = 0,
34
35 // 16 directives as OpenMP 3.0
36 e_parallel,
37 e_for,
38 e_for_simd,
39 e_do,
40 e_workshare,
41 e_sections,
42 e_section,
43 e_single,
44
45 e_master,
46 e_critical,
47 e_barrier,
48 e_atomic,
49 e_flush,
50
51 // Liao, 1/15/2013, experimental implementation for the draft OpenMP Accelerator Model technical report
52 e_target,
53 e_target_declare,
54 e_target_data,
55 e_target_update,
56 e_map, // map clauses
57 e_device,
58 e_begin, // 10/29/2015, experimental begin/end directives for SPMD code blocks, without changing variable scopes
59 e_end,
60
61 e_threadprivate,
62 e_parallel_for,
63 e_parallel_for_simd,
64 e_parallel_do,
65 e_parallel_sections,
66 e_parallel_workshare,
67 e_task,
68 e_taskwait,
69 // we have both ordered directive and ordered clause,
70 //so make the name explicit
71 e_ordered_directive,
72
73 // Fortran only end directives
74 e_end_critical,
75 e_end_do,
76 e_end_master,
77 e_end_ordered,
78 e_end_parallel_do,
79 e_end_parallel_sections,
80 e_end_parallel_workshare,
81 e_end_parallel,
82 e_end_sections,
83 e_end_single,
84 e_end_task,
85 e_end_workshare,
86
87 // 15 clauses for OpenMP 3.0
88 // 7 data-sharing attributes clauses
89 e_default, // the clause
90 e_shared,
91 e_private,
92 e_firstprivate,
93 e_lastprivate,
94 e_copyin,
95 e_copyprivate,
96 e_proc_bind,
97
98 //8 misc clauses
99 e_if, // used with omp parallel or omp task
100 e_num_threads, // for omp parallel only
101 e_nowait,
102 e_ordered_clause,
103 e_reduction,
104 e_schedule,
105 e_collapse,
106 e_untied,
107 e_mergeable,
108 e_final,
109 e_priority,
110 e_atomic_clause,
111 e_inbranch,
112 e_notinbranch,
113
114 e_depend, // OpenMP 4.0 task clauses
115
116 // Simple values for some clauses
117
118 //4 values for default clause
119 //C/C++ default values
120 e_default_none,
121 e_default_shared,
122 //Fortran default values
123 e_default_private,
124 e_default_firstprivate,
125
126 // proc_bind(master|close|spread)
127 e_proc_bind_master,
128 e_proc_bind_close,
129 e_proc_bind_spread,
130
131 e_atomic_read,
132 e_atomic_write,
133 e_atomic_update,
134 e_atomic_capture,
135
136 // reduction operations
137 //8 operand for C/C++
138 // shared 3 common operators for both C and Fortran
139 e_reduction_plus, //+
140 e_reduction_mul, //*
141 e_reduction_minus, // -
142
143 // C/C++ only
144 e_reduction_bitand, // &
145 e_reduction_bitor, // |
146 e_reduction_bitxor, // ^
147 e_reduction_logand, // &&
148 e_reduction_logor, // ||
149
150 // Fortran operator
151 e_reduction_and, // .and.
152 e_reduction_or, // .or.
153 e_reduction_eqv, // fortran .eqv.
154 e_reduction_neqv, // fortran .neqv.
155
156 // reduction intrinsic procedure name for Fortran
157 // min, max also for C
158 e_reduction_max,
159 e_reduction_min,
160
161 e_reduction_iand,
162 e_reduction_ior,
163 e_reduction_ieor,
164
165 //5 schedule policies for
166 //---------------------
167 e_schedule_none,
168 e_schedule_static,
169 e_schedule_dynamic,
170 e_schedule_guided,
171 e_schedule_auto,
172 e_schedule_runtime,
173
174 // 4 device map variants
175 //----------------------
176 e_map_alloc,
177 e_map_to,
178 e_map_from,
179 e_map_tofrom,
180
181 // experimental dist_data clause dist_data(dim1_policy, dim2_policy, dim3_policy)
182 // A policy can be block(n), cyclic(n), or duplicate
183 e_dist_data,
184 e_duplicate,
185 e_block,
186 e_cyclic,
187
188 // experimental SIMD directive, phlin 8/5/2013
189 e_simd,
190 e_declare_simd,
191 e_safelen,
192 e_simdlen,
193 e_uniform,
194 e_aligned,
195 e_linear,
196
197 // task dependence type
198 e_depend_in,
199 e_depend_out,
200 e_depend_inout,
201
202 // not an OpenMP construct
203 e_not_omp
204
205 }; //end omp_construct_enum
206
207 // A new variable to communicate the context of OpenMP parser
208 // what directive is being parsed right now.
209 // This is useful for rare case of parsing "declare simd"
210 extern omp_construct_enum cur_omp_directive;
211
212 //-------------------------------------------------------------------
213 // some utility functions
214
216 // Better using OmpSupport::toString() to avoid ambiguous
217 std::string toString(omp_construct_enum omp_type);
218
220 bool isFortranEndDirective(omp_construct_enum omp_type);
221
223 bool isFortranBeginDirective(omp_construct_enum omp_type);
224
226 bool isDirective(omp_construct_enum omp_type);
227
229 bool isDirectiveWithBody(omp_construct_enum omp_type);
230
232 bool isClause(omp_construct_enum omp_type);
233
235 bool isReductionOperator(omp_construct_enum omp_type);
236
238 bool isDependenceType(omp_construct_enum omp_type);
239
240 class OmpAttribute;
242 //
244 ROSE_DLL_API OmpAttribute* buildOmpAttribute(enum omp_construct_enum directive_type, SgNode* context_node, bool useDefined);
245
247 ROSE_DLL_API void addOmpAttribute(OmpAttribute* ompattribute, SgNode* node);
248
250 ROSE_DLL_API void removeOmpAttribute(OmpAttribute* ompattribute, SgNode* node);
251
254
255 class OmpAttributeList;
258
261
263 omp_construct_enum getOmpConstructEnum(SgPragmaDeclaration* decl);
264
266 omp_construct_enum getBeginOmpConstructEnum (omp_construct_enum end_enum);
267
269 omp_construct_enum getEndOmpConstructEnum (omp_construct_enum begin_enum);
270
272 ROSE_DLL_API void generatePragmaFromOmpAttribute(SgNode* sg_node);
273 //TODO this is duplicated from autoParallization project's generatedOpenMPPragmas()
274 // We should remove this duplicate once autopar is moved into rose/src
275
277 ROSE_DLL_API std::string generateDiffTextFromOmpAttribute(SgNode* sg_node);
278
279 //------------------------------------------------------------------
280 // By default, the persistent attribute attached to an OpenMP pragma node in SAGE III AST
281 // Attaching to pragma is easier since a few directives have no obvious
282 // associated code blocks, like threadprivate.
283 //
284 // The attribute can also be attached by a scope affected by OpenMP. This is used during
285 // automatic parallelization when the corresponding pragma is not yet generated.
286 //
287 // A cure-all approach is used to simplify the handling.
288 // OmpAttribute is implemented using a 'flat' data structure encompass all
289 // possible directives, clauses
290 // and their various contents, if any.
291 //
292 // different types of pragmas need different information in some cases
293 // e.g.
294 // 'omp for' needs scheduling type
295 //------------------------------------------------------------------
296
297 class ROSE_DLL_API OmpAttributeList :public AstAttribute
298 {
299 public:
300 std::vector<OmpAttribute*> ompAttriList;
301 // Restore to legal OpenMP directive strings
302 std::string toOpenMPString();
303 // Pretty print for debugging purpose
304 void print();
306
307 // This attribute attempts to manage its own memory by calling "delete" whenever the attribute is removed from an AST
308 // node. It avoids memory leaks by not allowing OmpAttributeList attributes to be copied (no virtual "copy"
309 // constructor), and it never tries to replace one OmpAttributeList object with another with AstAttributeMechanism's
310 // "set" or "replace" methods or the corresponding methods in SgNode. However, it leaks memory if an AST node is deleted.
311 virtual OwnershipPolicy getOwnershipPolicy() const override {
312 return CUSTOM_OWNERSHIP;
313 }
314 // MS2018: added to fix warning
315 virtual std::string attribute_class_name() const override;
316 virtual OmpAttributeList* copy() override;
317 };
318
320 class ROSE_DLL_API OmpAttribute
321 {
322 public:
326
329 void setPreprocessingInfo(PreprocessingInfo* info) { pinfo=info;};
330
332 SgNode* getNode(){return mNode;};
333 void setNode(SgNode* n) { mNode= n;};
335 void setOmpDirectiveType(omp_construct_enum omptype){ assert (isDirective(omptype)); omp_type = omptype;}
336 omp_construct_enum getOmpDirectiveType() {return omp_type;}
337
340 void addClause(omp_construct_enum clause_type);
342 bool hasClause(omp_construct_enum clause_type);
343
345 std::vector<omp_construct_enum> getClauses();
346
349 SgVariableSymbol* addVariable(omp_construct_enum targetConstruct, const std::string& varString, SgInitializedName* sgvar=NULL);
350
352 SgVariableSymbol* addVariable(omp_construct_enum targetConstruct, SgExpression* varExp);
353
355 bool hasVariableList(omp_construct_enum);
357 std::vector<std::pair<std::string,SgNode* > >
358 getVariableList(omp_construct_enum);
359
361 // We store a list (vector) of dimension bounds for each array variable
362 std::map<SgSymbol*, std::vector < std::pair <SgExpression*, SgExpression*> > > array_dimensions;
363
364 // dist_data (dim1_policy, dim2_policy, dim3_policy) for mapped arrays
365 // we use std::map <variable, policy_vector> to represent this.
366 // the policy vector contains up to three pair of (policy, optional size)
367 // e.g. map(x[0:n][0:m] dist_data(duplicate, block(2)))
368 // -----------------------------------
369 std::map <SgSymbol* , std::vector < std::pair<omp_construct_enum, SgExpression*> > > dist_data_policies;
370
372 std::vector<enum omp_construct_enum> get_clauses(const std::string& variable);
373
375 bool appendDistDataPolicy(SgVariableSymbol* array_symbol, omp_construct_enum dist_data_policy, SgExpression* size_exp = NULL);
376
378 std::vector < std::pair < omp_construct_enum, SgExpression*> > getDistDataPolicy (SgVariableSymbol* array_symbol);
379
382 void addExpression(omp_construct_enum targetConstruct, const std::string& expString, SgExpression* sgexp=NULL);
383
385 std::pair<std::string, SgExpression*>
386 getExpression(omp_construct_enum targetConstruct);
387
389 //
390 // Reduction needs special handling
391 // since multiple ones with different operator types can co-exist within one pragma
392 // We categories reduction clauses by their operator type and store variable lists for each of the reduction operator type, not with the reduction clause
393
394 // Add a new reduction clause with the specified operator
395 void setReductionOperator(omp_construct_enum operatorx);
396
398 std::vector<omp_construct_enum> getReductionOperators();
399
401 bool hasReductionOperator(omp_construct_enum operatorx);
402
403 //------------------------------------------
404 // Add a new clause with the specified operator
405 void setDependenceType(omp_construct_enum operatorx);
406
408 std::vector<omp_construct_enum> getDependenceTypes();
409
411 bool hasDependenceType(omp_construct_enum operatorx);
412
413
414 // map clause is similar to reduction clause,
415 //
416 // Add a new map clauses with the specified variant type
417 void setMapVariant(omp_construct_enum operatorx);
419 std::vector<omp_construct_enum> getMapVariants();
421 bool hasMapVariant(omp_construct_enum operatorx);
422
424 bool isMapVariant(omp_construct_enum omp_type);
425
426 // default () value
427 void setDefaultValue(omp_construct_enum valuex);
428 omp_construct_enum getDefaultValue();
429
430 // proc_bind() policy
431 void setProcBindPolicy(omp_construct_enum valuex);
432 omp_construct_enum getProcBindPolicy();
433
434 //Atomicity of Atomic Clause
435 void setAtomicAtomicity(omp_construct_enum valuex);
436 omp_construct_enum getAtomicAtomicity();
437
438 // Schedule kind
439 omp_construct_enum getScheduleKind();
440 void setScheduleKind(omp_construct_enum kindx);
441
443 bool isInConstruct(const std::string & variable, enum omp_construct_enum);
444
446 void setCriticalName(const std::string & name);
447 std::string getCriticalName() {return name;};
448 bool isNamedCritical(){return hasName;};
449
451 void print();
452
454 bool get_isUserDefined() {return isUserDefined; }
455
457 //not named toString() to void ambiguous with OmpAttribute::toString()
458 std::string toOpenMPString();
459 friend OmpAttribute* buildOmpAttribute(omp_construct_enum directive_type, SgNode* node, bool userDefined);
460 //------------------hide the implementation details, could be changed anytime!!
461 //----------------------------------------------------------------------------
462 private:
463 //It is recommended to use OmpSupport::buildOmpAttribute() instead of
464 //using the constructors here
467 {
468 mNode = NULL;
469 omp_type = e_unknown;
470 init();
471 isUserDefined = true;
472 }
474 OmpAttribute(omp_construct_enum omptype, SgNode* mynode):
475 mNode(mynode),omp_type(omptype){
476 /*The initialization order has to match the declaration order,
477 * otherwise get a compilation warning*/
478 init();
479 isUserDefined = true;
480 if (mNode != NULL )
481 {
482#if 0 // [Robb Matzke 2021-03-28]: causes unused variable warning every time this header is included
483 SgLocatedNode * lnode = isSgLocatedNode (mNode);
484 ROSE_ASSERT (lnode != NULL);
485 //ROSE_ASSERT (lnode->get_file_info()->get_filename()!=std::string("transformation"));
486#else // fixed version
487 ROSE_ASSERT(isSgLocatedNode(mNode));
488#endif
489 }
490 // Liao 2/12/2010, we allow build empty attribute as a replacement of a default constructor.
491 // This is used by autoParallization to tentatively create an instance and later fill data fields.
492 // assert(isDirective(omptype));
493 }
494
496 SgNode* mNode;
498 PreprocessingInfo* pinfo;
499
501 bool isUserDefined;
502
504 enum omp_construct_enum omp_type;
505
507 // vector is used to preserve the order of clauses in the directive
508 // map is used to fast query if a clause exists or not
509 // Some clauses are allowed to appear more than once, merge the content into the first occurrence in our implementation.
510 std::vector<omp_construct_enum> clauses;
511 std::map<omp_construct_enum,bool> clause_map;
512
513 // Multiple reduction clauses, each has a different operator
514 //value for reduction operation: + -, * & | etc
515 std::vector<omp_construct_enum> reduction_operators;
516
518 std::vector<omp_construct_enum> dependence_types;
519
520 // Liao, 1/15/2013, map variant:
521 // there could be multiple map clause with the same variant type: alloc, to, from , and tofrom.
522 std::vector<omp_construct_enum> map_variants;
523 //enum omp_construct_enum map_variant;
524
525 //variable lists-------------------
526 //appeared within some directives and clauses
527 //The clauses/directive are: flush, threadprivate, private, firstprivate,
528 // shared, copyin, reduction, lastprivate, copyprivate
529 // We use a pair of (name, SgNode) for each variable
530 // It is highly possible that a variable having more than one OpenMP properties.
531 // For example, a variable can be both firstprivate and lastprivate.
532 std::map<omp_construct_enum, std::vector<std::pair<std::string,SgNode* > > > variable_lists;
533 // A reverse map from a variable to the clauses the variable appears
534 std::map<std::string, std::vector<omp_construct_enum> > var_clauses;
535
536
537 // expressions ----------------------
538 // e.g.: if (exp), num_threads(exp), schedule(,exp), collapse(exp)
539 std::map<omp_construct_enum, std::pair<std::string, SgExpression*> > expressions;
540
541 // values for some clauses -------------------------
542 // values for default() clause: data scoping information
543 // choices are: none,shared, private, firstprivate
544 omp_construct_enum default_scope;
545
546 // values for proc_bind() clause
547 omp_construct_enum proc_bind_policy;
548
549 // Atomic clause's atomicity
550 omp_construct_enum atomicity;
551
552 // value for omp for's schedule policies
553 omp_construct_enum schedule_kind;
554
555 // Only used for omp critical to indicate if it is named or not
556 // name for the directive, only used for omp critical
557 bool hasName;
558 std::string name;
559
560 // Misc fields --------------------------------
561 // help translation and analysis
562 bool isOrphaned; //true if parent omp parallel is not in the static lexical scope
563
564 // Additional information to help translation
565 int wrapperCount; // the shared variables from the same scope which needs wrapper
566
567 //optional information
568 OmpAttribute * parent; //upper-level OMP pragma's attribute
569
572 void init() ;
573
575 // invoke OmpSupport::toString() to stringify the enumerate type internally
576 // some variables have the optional dist_data policy
577 std::string toOpenMPString(omp_construct_enum omp_type);
578
580 std::string toOpenMPString(std::vector<std::pair<std::string,SgNode* > > varList, bool checkDistPolicy = false);
581
583 std::string toOpenMPString (std::vector < std::pair <omp_construct_enum, SgExpression*> > dim_policies);
584 }; // end class OmpAttribute
585
586
587 // save encountered Fortran OpenMP directives here.
588 // We reuse the list later on to build OpenMP AST for Fortran
589 extern std::list<OmpAttribute* > omp_comment_list;
590
591
592} //end namespace OmpSupport
593
594#endif //Omp_Attribute_h_INCLUDED
595
Base class for all IR node attribute values.
OwnershipPolicy
Who owns this attribute.
virtual OwnershipPolicy getOwnershipPolicy() const override
Who owns this attribute.
virtual std::string attribute_class_name() const override
Attribute class name.
One attribute object stores all information within an OpenMP pragma (directive and clauses)
void addExpression(omp_construct_enum targetConstruct, const std::string &expString, SgExpression *sgexp=NULL)
-----—Expressions --------------------------— Add an expression to a clause
SgVariableSymbol * addVariable(omp_construct_enum targetConstruct, const std::string &varString, SgInitializedName *sgvar=NULL)
-----—var list -----------— Add a variable into a variable list of an OpenMP construct ,...
PreprocessingInfo * getPreprocessingInfo()
Get the associated PreprocessingInfo for Fortran, if any
std::vector< omp_construct_enum > getMapVariants()
Get map clauses for each variant, map(variant:var_list)
std::map< SgSymbol *, std::vector< std::pair< SgExpression *, SgExpression * > > > array_dimensions
Dimension information for array variables, used by map clause, such as map (tofrom:array[0:n][0:m])
bool hasMapVariant(omp_construct_enum operatorx)
Check if a map variant exists.
std::vector< omp_construct_enum > getDependenceTypes()
Get dependence clauses for each type, depend(type:varlist)
void setReductionOperator(omp_construct_enum operatorx)
-----—values for some clauses -------—
void setCriticalName(const std::string &name)
Set name for named critical section.
friend OmpAttribute * buildOmpAttribute(omp_construct_enum directive_type, SgNode *node, bool userDefined)
Some utility functions to manipulate OmpAttribute.
std::vector< std::pair< std::string, SgNode * > > getVariableList(omp_construct_enum)
Get the variable list associated with a construct.
bool hasDependenceType(omp_construct_enum operatorx)
Check if a depend type exists.
bool isMapVariant(omp_construct_enum omp_type)
Check if the input parameter is a map variant enum type.
bool hasClause(omp_construct_enum clause_type)
Check if a directive has a clause of the specified type.
void addClause(omp_construct_enum clause_type)
--------—clauses-------------— Add a clause into an OpenMP directive, the content of the clause is se...
std::vector< omp_construct_enum > getClauses()
Get all existing clauses.
bool hasVariableList(omp_construct_enum)
Check if a variable list is associated with a construct.
std::vector< std::pair< omp_construct_enum, SgExpression * > > getDistDataPolicy(SgVariableSymbol *array_symbol)
Obtain data distribution policy for an array. There are up to 3 pairs for 3-D.
SgNode * getNode()
Get the associated SgNode, can be SgPragmaDeclaration or others( for fortran nodes or during parallel...
SgPragmaDeclaration * getPragmaDeclaration()
-----------—AST connection---------------— Get the associated SgPragmaDeclaration for C/C++,...
std::pair< std::string, SgExpression * > getExpression(omp_construct_enum targetConstruct)
Get expression of a clause.
std::vector< omp_construct_enum > getReductionOperators()
Get reduction clauses for each operations, reduction(op:kind)
bool appendDistDataPolicy(SgVariableSymbol *array_symbol, omp_construct_enum dist_data_policy, SgExpression *size_exp=NULL)
Insert dist_data policy for one dimension of an array into its policy vector (duplicate,...
std::string toOpenMPString()
Convert OmpAttribute to a legal OpenMP pragma string,.
bool isInConstruct(const std::string &variable, enum omp_construct_enum)
Check if a variable is inside a variable list of a clause/directive.
std::vector< enum omp_construct_enum > get_clauses(const std::string &variable)
Find the relevant clauses for a variable.
void setOmpDirectiveType(omp_construct_enum omptype)
---------—directive type----—
bool hasReductionOperator(omp_construct_enum operatorx)
Check if a reduction operation exists.
void print()
Pretty print the OmpAttribute.
SgVariableSymbol * addVariable(omp_construct_enum targetConstruct, SgExpression *varExp)
Add a variable ref expression to a clause: this is useful for array reference expression....
For preprocessing information including source comments, include , if, define, etc.
This class represents the notion of an expression. Expressions are derived from SgLocatedNodes,...
This class represents the notion of a declared variable.
This class represents the notion of an expression or statement which has a position within the source...
This class represents the base class for all IR nodes within Sage III.
This class represents the concept of a C Assembler statement (untested).
This class represents the concept of a variable name within the compiler (a shared container for the ...
Types and functions to support OpenMP.
bool isDirective(omp_construct_enum omp_type)
Check if an OpenMP construct is a directive.
bool isDirectiveWithBody(omp_construct_enum omp_type)
Check if an OpenMP directive has a structured body.
OmpAttribute * getOmpAttribute(SgNode *node)
Get the first OmpAttribute from a SgNode, return NULL if not found.
ROSE_DLL_API void removeOmpAttribute(OmpAttribute *ompattribute, SgNode *node)
Remove OmpAttribute from a SgNode.
ROSE_DLL_API std::string generateDiffTextFromOmpAttribute(SgNode *sg_node)
Generate diff text from OmpAttribute attached to a statement.
omp_construct_enum getOmpConstructEnum(SgPragmaDeclaration *decl)
Get omp enum from an OpenMP pragma attached with OmpAttribute.
omp_construct_enum getBeginOmpConstructEnum(omp_construct_enum end_enum)
Get the corresponding begin construct enum from an end construct enum.
bool isFortranBeginDirective(omp_construct_enum omp_type)
Check if the construct is a Fortran directive which can (optionally) have a corresponding END directi...
ROSE_DLL_API void addOmpAttribute(OmpAttribute *ompattribute, SgNode *node)
Add OmpAttribute to a SgNode.
ROSE_DLL_API void generatePragmaFromOmpAttribute(SgNode *sg_node)
Generate a pragma declaration from OmpAttribute attached to a statement.
bool isDependenceType(omp_construct_enum omp_type)
Check if an OpenMP construct is a dependence type for omp task depend.
std::string toString(omp_construct_enum omp_type)
Output omp_construct_enum to a string:
ROSE_DLL_API bool isEquivalentOmpAttribute(OmpAttribute *a1, OmpAttribute *a2)
Check if two OmpAttributes are semantically equivalent to each other.
ROSE_DLL_API OmpAttribute * buildOmpAttribute(enum omp_construct_enum directive_type, SgNode *context_node, bool useDefined)
Some utility functions to manipulate OmpAttribute.
bool isReductionOperator(omp_construct_enum omp_type)
Check if an OpenMP construct is a reduction operator.
ROSE_DLL_API OmpAttributeList * getOmpAttributeList(SgNode *node)
Get OmpAttribute from a SgNode, return NULL if not found.
bool isFortranEndDirective(omp_construct_enum omp_type)
Check if the construct is a Fortran END ... directive.
bool isClause(omp_construct_enum omp_type)
Check if an OpenMP construct is a clause.
omp_construct_enum getEndOmpConstructEnum(omp_construct_enum begin_enum)
Get the corresponding end construct enum from a begin construct enum.