1 /*
   2  * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 #ifndef SHARE_ADLC_FORMSOPT_HPP
  26 #define SHARE_ADLC_FORMSOPT_HPP
  27 
  28 // FORMSOPT.HPP - ADL Parser Target Specific Optimization Forms Classes
  29 
  30 // Class List
  31 class Form;
  32 class InstructForm;
  33 class OperandForm;
  34 class OpClassForm;
  35 class AttributeForm;
  36 class RegisterForm;
  37 class PipelineForm;
  38 class SourceForm;
  39 class EncodeForm;
  40 class Component;
  41 class Constraint;
  42 class Predicate;
  43 class MatchRule;
  44 class Attribute;
  45 class Effect;
  46 class ExpandRule;
  47 class RewriteRule;
  48 class ConstructRule;
  49 class FormatRule;
  50 class Peephole;
  51 class PeepMatch;
  52 class PeepConstraint;
  53 class EncClass;
  54 class Interface;
  55 class RegInterface;
  56 class ConstInterface;
  57 class MemInterface;
  58 class CondInterface;
  59 class Opcode;
  60 class InsEncode;
  61 class RegDef;
  62 class RegClass;
  63 class CodeSnippetRegClass;
  64 class ConditionalRegClass;
  65 class AllocClass;
  66 class ResourceForm;
  67 class PipeClassForm;
  68 class PipeClassOperandForm;
  69 class PipeClassResourceForm;
  70 class PeepMatch;
  71 class PeepConstraint;
  72 class PeepReplace;
  73 class MatchList;
  74 
  75 class ArchDesc;
  76 
  77 //==============================Register Allocation============================
  78 //------------------------------RegisterForm-----------------------------------
  79 class RegisterForm : public Form {
  80 private:
  81   AllocClass *_current_ac;         // State used by iter_RegDefs()
  82 
  83 public:
  84   // Public Data
  85   NameList    _rdefs;              // List of register definition names
  86   Dict        _regDef;             // map register name to RegDef*
  87 
  88   NameList    _rclasses;           // List of register class names
  89   Dict        _regClass;           // map register class name to RegClass*
  90 
  91   NameList    _aclasses;           // List of allocation class names
  92   Dict        _allocClass;         // Dictionary of allocation classes
  93 
  94   static int  _reg_ctr;         // Register counter
  95   static int  RegMask_Size();   // Compute RegMask size
  96 
  97   // Public Methods
  98   RegisterForm();
  99   ~RegisterForm();
 100 
 101   void        addRegDef(char *regName, char *callingConv, char *c_conv,
 102                         char * idealtype, char *encoding, char* concreteName);
 103   template<typename T> T* addRegClass(const char* className);
 104 
 105   AllocClass *addAllocClass(char *allocName);
 106   void        addSpillRegClass();
 107 
 108   // Provide iteration over all register definitions
 109   // in the order used by the register allocator
 110   void        reset_RegDefs();
 111   RegDef     *iter_RegDefs();
 112   RegDef     *getRegDef  (const char *regName);
 113 
 114   RegClass   *getRegClass(const char *className);
 115 
 116   // Return register mask, compressed chunk and register #
 117   uint       reg_mask(char *register_class);
 118 
 119   // Check that register classes are compatible with chunks
 120   bool       verify();
 121 
 122   void dump();                     // Debug printer
 123   void output(FILE *fp);           // Write info to output files
 124 };
 125 
 126 //------------------------------RegDef-----------------------------------------
 127 class RegDef : public Form {
 128 public:
 129   // Public Data
 130   const char *_regname;            // ADLC (Opto) Register name
 131   const char *_callconv;           // Calling convention
 132   const char *_c_conv;             // Native calling convention, 'C'
 133   const char *_idealtype;          // Ideal Type for register save/restore
 134   const char *_concrete;           // concrete register name
 135 
 136 private:
 137   const char *_register_encode;   // The register encoding
 138   // The chunk and register mask bits define info for register allocation
 139   uint32      _register_num;      // Which register am I
 140 
 141 public:
 142   // Public Methods
 143   RegDef(char  *regname, char *callconv, char *c_conv,
 144          char *idealtype, char *encoding, char *concrete);
 145   ~RegDef();                       // Destructor
 146 
 147   // Interface to define/redefine the register number
 148   void     set_register_num(uint32 new_register_num);
 149 
 150   // Bit pattern used for generating machine code
 151   const char *register_encode()   const;
 152   // Register number used in machine-independent code
 153   uint32   register_num()      const;
 154 
 155   void dump();                     // Debug printer
 156   void output(FILE *fp);           // Write info to output files
 157 };
 158 
 159 //------------------------------RegClass---------------------------------------
 160 // Generic register class. This register class is the internal representation
 161 // for the following .ad file format:
 162 //
 163 //  reg_class ptr(RAX, RBX, ...);
 164 //
 165 // where ptr is the name of the register class, RAX and RBX are registers.
 166 //
 167 // This register class allows registers to be spilled onto the stack. Spilling
 168 // is allowed is field _stack_or_reg is true.
 169 class RegClass : public Form {
 170 public:
 171   // Public Data
 172   const char *_classid;         // Name of class
 173   NameList    _regDefs;         // List of registers in class
 174   Dict        _regDef;          // Dictionary of registers in class
 175 protected:
 176   bool        _stack_or_reg;    // Allowed on any stack slot
 177 
 178 public:
 179   // Public Methods
 180   RegClass(const char *classid);// Constructor
 181   virtual ~RegClass();
 182 
 183   void addReg(RegDef *regDef);  // Add a register to this class
 184 
 185   uint size() const;            // Number of registers in class
 186   int regs_in_word( int wordnum, bool stack_also );
 187 
 188   const RegDef *get_RegDef(const char *regDef_name) const;
 189 
 190   // Returns the lowest numbered register in the mask.
 191   const RegDef* find_first_elem();
 192 
 193   // Iteration support
 194   void          reset();        // Reset the following two iterators
 195   RegDef       *RegDef_iter();  // which move jointly,
 196   const char   *rd_name_iter(); // invoking either advances both.
 197 
 198   void dump();                  // Debug printer
 199   void output(FILE *fp);        // Write info to output files
 200 
 201   virtual bool has_stack_version() {
 202     return _stack_or_reg;
 203   }
 204   virtual void set_stack_version(bool flag) {
 205     _stack_or_reg = flag;
 206   }
 207 
 208   virtual void declare_register_masks(FILE* fp);
 209   virtual void build_register_masks(FILE* fp);
 210 };
 211 
 212 //------------------------------CodeSnippetRegClass----------------------------
 213 // Register class that has an user-defined C++ code snippet attached to it
 214 // to determine at runtime which register class to use. This register class is
 215 // the internal representation for the following .ad file format:
 216 //
 217 //  reg_class actual_dflt_reg %{
 218 //      if (VM_Version::has_vfp3_32()) {
 219 //          return DFLT_REG_mask();
 220 //      } else {
 221 //          return DFLT_LOW_REG_mask();
 222 //      }
 223 //  %}
 224 //
 225 // where DFLT_REG_mask() and DFLT_LOW_REG_mask() are the internal names of the
 226 // masks of register classes dflt_reg and dflt_low_reg.
 227 //
 228 // The attached code snippet can select also between more than two register classes.
 229 // This register class can be, however, used only if the register class is not
 230 // cisc-spillable (i.e., the registers of this class are not allowed on the stack,
 231 // which is equivalent with _stack_or_reg being false).
 232 class CodeSnippetRegClass : public RegClass {
 233 protected:
 234   char* _code_snippet;
 235 public:
 236   CodeSnippetRegClass(const char* classid);// Constructor
 237   ~CodeSnippetRegClass();
 238 
 239   void set_code_snippet(char* code) {
 240     _code_snippet = code;
 241   }
 242   char* code_snippet() {
 243     return _code_snippet;
 244   }
 245   void set_stack_version(bool flag) {
 246     assert(false, "User defined register classes are not allowed to spill to the stack.");
 247   }
 248   void declare_register_masks(FILE* fp);
 249   void build_register_masks(FILE* fp) {
 250     // We do not need to generate register masks because we select at runtime
 251     // between register masks generated for other register classes.
 252     return;
 253   }
 254 };
 255 
 256 //------------------------------ConditionalRegClass----------------------------
 257 // Register class that has two register classes and a runtime condition attached
 258 // to it. The condition is evaluated at runtime and either one of the register
 259 // attached register classes is selected. This register class is the internal
 260 // representation for the following .ad format:
 261 //
 262 //  reg_class_dynamic actual_dflt_reg(dflt_reg, low_reg,
 263 //                                    %{ VM_Version::has_vfp3_32() }%
 264 //                                    );
 265 //
 266 // This example is equivalent to the example used with the CodeSnippetRegClass
 267 // register class. A ConditionalRegClass works also if a register class is cisc-spillable
 268 // (i.e., _stack_or_reg is true), but if can select only between two register classes.
 269 class ConditionalRegClass : public RegClass {
 270 protected:
 271   // reference to condition code
 272   char* _condition_code;  // C++ condition code to dynamically determine which register class to use.
 273 
 274                           // Example syntax (equivalent to previous example):
 275                           //
 276                           //  reg_class actual_dflt_reg(dflt_reg, low_reg,
 277                           //                            %{ VM_Version::has_vfp3_32() }%
 278                           //                            );
 279   // reference to conditional register classes
 280   RegClass* _rclasses[2]; // 0 is the register class selected if the condition code returns true
 281                           // 1 is the register class selected if the condition code returns false
 282 public:
 283   ConditionalRegClass(const char* classid);// Constructor
 284   ~ConditionalRegClass();
 285 
 286   virtual void set_stack_version(bool flag) {
 287     RegClass::set_stack_version(flag);
 288     assert((_rclasses[0] != NULL), "Register class NULL for condition code == true");
 289     assert((_rclasses[1] != NULL), "Register class NULL for condition code == false");
 290     _rclasses[0]->set_stack_version(flag);
 291     _rclasses[1]->set_stack_version(flag);
 292   }
 293   void declare_register_masks(FILE* fp);
 294   void build_register_masks(FILE* fp) {
 295     // We do not need to generate register masks because we select at runtime
 296     // between register masks generated for other register classes.
 297     return;
 298   }
 299   void set_rclass_at_index(int index, RegClass* rclass) {
 300     assert((0 <= index && index < 2), "Condition code can select only between two register classes");
 301     _rclasses[index] = rclass;
 302   }
 303   void set_condition_code(char* code) {
 304     _condition_code = code;
 305   }
 306   char* condition_code() {
 307     return _condition_code;
 308   }
 309 };
 310 
 311 //------------------------------AllocClass-------------------------------------
 312 class AllocClass : public Form {
 313 private:
 314 
 315 public:
 316   // Public Data
 317   char    *_classid;            // Name of class
 318   NameList _regDefs;            // List of registers in class
 319   Dict     _regDef;             // Dictionary of registers in class
 320 
 321   // Public Methods
 322   AllocClass(char *classid);    // Constructor
 323 
 324   void addReg(RegDef *regDef);  // Add a register to this class
 325   uint size() {return _regDef.Size();} // Number of registers in class
 326 
 327   void dump();                  // Debug printer
 328   void output(FILE *fp);        // Write info to output files
 329 };
 330 
 331 
 332 //==============================Frame Handling================================
 333 //------------------------------FrameForm-------------------------------------
 334 class FrameForm : public Form {
 335 private:
 336 
 337 public:
 338   // Public Data
 339   bool  _direction;                // Direction of stack growth
 340   char *_sync_stack_slots;
 341   char *_inline_cache_reg;
 342   char *_interpreter_method_oop_reg;
 343   char *_interpreter_frame_pointer_reg;
 344   char *_cisc_spilling_operand_name;
 345   char *_frame_pointer;
 346   char *_c_frame_pointer;
 347   char *_alignment;
 348   bool  _return_addr_loc;
 349   bool  _c_return_addr_loc;
 350   char *_return_addr;
 351   char *_c_return_addr;
 352   char *_in_preserve_slots;
 353   char *_varargs_C_out_slots_killed;
 354   char *_calling_convention;
 355   char *_c_calling_convention;
 356   char *_return_value;
 357   char *_c_return_value;
 358 
 359   // Public Methods
 360   FrameForm();
 361   ~FrameForm();
 362 
 363   void dump();                     // Debug printer
 364   void output(FILE *fp);           // Write info to output files
 365 };
 366 
 367 
 368 //==============================Scheduling=====================================
 369 //------------------------------PipelineForm-----------------------------------
 370 class PipelineForm : public Form {
 371 private:
 372 
 373 public:
 374   // Public Data
 375   NameList   _reslist;            // List of pipeline resources
 376   FormDict   _resdict;            // Resource Name -> ResourceForm mapping
 377   int        _rescount;           // Number of resources (ignores OR cases)
 378   int        _maxcycleused;       // Largest cycle used relative to beginning of instruction
 379 
 380   NameList   _stages;             // List of pipeline stages on architecture
 381   int        _stagecnt;           // Number of stages listed
 382 
 383   NameList   _classlist;          // List of pipeline classes
 384   FormDict   _classdict;          // Class Name -> PipeClassForm mapping
 385   int        _classcnt;           // Number of classes
 386 
 387   NameList   _noplist;            // List of NOP instructions
 388   int        _nopcnt;             // Number of nop instructions
 389 
 390   bool       _variableSizeInstrs; // Indicates if this architecture has variable sized instructions
 391   bool       _branchHasDelaySlot; // Indicates that branches have delay slot instructions
 392   int        _maxInstrsPerBundle; // Indicates the maximum number of instructions for ILP
 393   int        _maxBundlesPerCycle; // Indicates the maximum number of bundles for ILP
 394   int        _instrUnitSize;      // The minimum instruction unit size, in bytes
 395   int        _bundleUnitSize;     // The bundle unit size, in bytes
 396   int        _instrFetchUnitSize; // The size of the I-fetch unit, in bytes [must be power of 2]
 397   int        _instrFetchUnits;    // The number of I-fetch units processed per cycle
 398 
 399   // Public Methods
 400   PipelineForm();
 401   ~PipelineForm();
 402 
 403   void dump();                    // Debug printer
 404   void output(FILE *fp);          // Write info to output files
 405 };
 406 
 407 //------------------------------ResourceForm-----------------------------------
 408 class ResourceForm : public Form {
 409 public:
 410   unsigned mask() const { return _resmask; };
 411 
 412 private:
 413   // Public Data
 414   unsigned _resmask;         // Resource Mask (OR of resource specifier bits)
 415 
 416 public:
 417 
 418   // Virtual Methods
 419   virtual ResourceForm  *is_resource()    const;
 420 
 421   // Public Methods
 422   ResourceForm(unsigned resmask); // Constructor
 423   ~ResourceForm();                // Destructor
 424 
 425   void dump();                    // Debug printer
 426   void output(FILE *fp);          // Write info to output files
 427 };
 428 
 429 //------------------------------PipeClassOperandForm-----------------------------
 430 class PipeClassOperandForm : public Form {
 431 private:
 432 
 433 public:
 434   // Public Data
 435   const char *_stage;             // Name of Stage
 436   unsigned _iswrite;              // Read or Write
 437   unsigned _more_instrs;          // Additional Instructions
 438 
 439   // Public Methods
 440   PipeClassOperandForm(const char *stage, unsigned iswrite, unsigned more_instrs)
 441   : _stage(stage)
 442   , _iswrite(iswrite)
 443   , _more_instrs(more_instrs)
 444  {};
 445 
 446   ~PipeClassOperandForm() {};     // Destructor
 447 
 448   bool isWrite() const { return _iswrite != 0; }
 449 
 450   void dump();                    // Debug printer
 451   void output(FILE *fp);          // Write info to output files
 452 };
 453 
 454 //------------------------------PipeClassResourceForm--------------------------
 455 class PipeClassResourceForm : public Form {
 456 private:
 457 
 458 public:
 459   // Public Data
 460   const char *_resource;          // Resource
 461   const char *_stage;             // Stage the resource is used in
 462   int         _cycles;            // Number of cycles the resource is used
 463 
 464   // Public Methods
 465   PipeClassResourceForm(const char *resource, const char *stage, int cycles)
 466                                   // Constructor
 467     : _resource(resource)
 468     , _stage(stage)
 469     , _cycles(cycles)
 470     {};
 471 
 472   ~PipeClassResourceForm() {};    // Destructor
 473 
 474   void dump();                    // Debug printer
 475   void output(FILE *fp);          // Write info to output files
 476 };
 477 
 478 //------------------------------PipeClassForm----------------------------------
 479 class PipeClassForm : public Form {
 480 private:
 481 
 482 public:
 483 
 484   // Public Data
 485   const char       *_ident;             // Name of class
 486   int               _num;               // Used in name of MachNode subclass
 487   NameList          _parameters;        // Locally defined names
 488   FormDict          _localNames;        // Table of operands & their types
 489   FormDict          _localUsage;        // Table of operand usage
 490   FormList          _resUsage;          // List of resource usage
 491   NameList          _instructs;         // List of instructions and machine nodes that use this pipeline class
 492   bool              _has_fixed_latency; // Always takes this number of cycles
 493   int               _fixed_latency;     // Always takes this number of cycles
 494   int               _instruction_count; // Number of instructions in first bundle
 495   bool              _has_multiple_bundles;  // Indicates if 1 or multiple bundles
 496   bool              _has_branch_delay_slot; // Has branch delay slot as last instruction
 497   bool              _force_serialization;   // This node serializes relative to surrounding nodes
 498   bool              _may_have_no_code;      // This node may generate no code based on register allocation
 499 
 500   // Virtual Methods
 501   virtual PipeClassForm  *is_pipeclass()    const;
 502 
 503   // Public Methods
 504   PipeClassForm(const char *id, int num);
 505                                   // Constructor
 506   ~PipeClassForm();               // Destructor
 507 
 508   bool hasFixedLatency() { return _has_fixed_latency; }
 509   int fixedLatency() { return _fixed_latency; }
 510 
 511   void setFixedLatency(int fixed_latency) { _has_fixed_latency = 1; _fixed_latency = fixed_latency; }
 512 
 513   void setInstructionCount(int i)    { _instruction_count = i; }
 514   void setMultipleBundles(bool b)    { _has_multiple_bundles = b; }
 515   void setBranchDelay(bool s)        { _has_branch_delay_slot = s; }
 516   void setForceSerialization(bool s) { _force_serialization = s; }
 517   void setMayHaveNoCode(bool s)      { _may_have_no_code = s; }
 518 
 519   int  InstructionCount()   const { return _instruction_count; }
 520   bool hasMultipleBundles() const { return _has_multiple_bundles; }
 521   bool hasBranchDelay()     const { return _has_branch_delay_slot; }
 522   bool forceSerialization() const { return _force_serialization; }
 523   bool mayHaveNoCode()      const { return _may_have_no_code; }
 524 
 525   void dump();                    // Debug printer
 526   void output(FILE *fp);          // Write info to output files
 527 };
 528 
 529 
 530 //==============================Peephole Optimization==========================
 531 //------------------------------Peephole---------------------------------------
 532 class Peephole : public Form {
 533 private:
 534   static int      _peephole_counter;// Incremented by each peephole rule parsed
 535   int             _peephole_number;// Remember my order in architecture description
 536   PeepMatch      *_match;          // Instruction pattern to match
 537   PeepConstraint *_constraint;     // List of additional constraints
 538   PeepReplace    *_replace;        // Instruction pattern to substitute in
 539 
 540   Peephole *_next;
 541 
 542 public:
 543   // Public Methods
 544   Peephole();
 545   ~Peephole();
 546 
 547   // Append a peephole rule with the same root instruction
 548   void append_peephole(Peephole *next_peephole);
 549 
 550   // Store the components of this peephole rule
 551   void add_match(PeepMatch *only_one_match);
 552   void append_constraint(PeepConstraint *next_constraint);
 553   void add_replace(PeepReplace *only_one_replacement);
 554 
 555   // Access the components of this peephole rule
 556   int             peephole_number() { return _peephole_number; }
 557   PeepMatch      *match()       { return _match; }
 558   PeepConstraint *constraints() { return _constraint; }
 559   PeepReplace    *replacement() { return _replace; }
 560   Peephole       *next()        { return _next; }
 561 
 562   void dump();                     // Debug printer
 563   void output(FILE *fp);           // Write info to output files
 564 };
 565 
 566 
 567 class PeepMatch : public Form {
 568 private:
 569   char *_rule;
 570   // NameList  _depth;                // Depth of this instruction
 571   NameList  _parent;
 572   NameList  _position;
 573   NameList  _instrs;               // List of instructions in match rule
 574   NameList  _input;                // input position in parent's instruction
 575   int       _max_position;
 576 
 577 public:
 578   // Public Methods
 579   PeepMatch(char *rule);
 580   ~PeepMatch();
 581 
 582   // Insert info into the match-rule
 583   void  add_instruction(int parent, int position, const char *name, int input);
 584 
 585   // Access info about instructions in the peep-match rule
 586   int   max_position();
 587   const char *instruction_name(int position);
 588   // Iterate through all info on matched instructions
 589   void  reset();
 590   void  next_instruction(int &parent, int &position, const char* &name, int &input);
 591   // 'true' if current position in iteration is a placeholder, not matched.
 592   bool  is_placeholder();
 593 
 594   void dump();
 595   void output(FILE *fp);
 596 };
 597 
 598 
 599 class PeepConstraint : public Form {
 600 private:
 601   PeepConstraint *_next;           // Additional constraints ANDed together
 602 
 603 public:
 604   const int   _left_inst;
 605   const char* _left_op;
 606   const char* _relation;
 607   const int   _right_inst;
 608   const char* _right_op;
 609 
 610 public:
 611   // Public Methods
 612   PeepConstraint(int left_inst,  char* left_op, char* relation,
 613                  int right_inst, char* right_op);
 614   ~PeepConstraint();
 615 
 616   // Check if constraints use instruction at position
 617   bool constrains_instruction(int position);
 618 
 619   // Add another constraint
 620   void append(PeepConstraint *next_peep_constraint);
 621   // Access the next constraint in the list
 622   PeepConstraint *next();
 623 
 624   void dump();
 625   void output(FILE *fp);
 626 };
 627 
 628 
 629 class PeepReplace : public Form {
 630 private:
 631   char *_rule;
 632   NameList _instruction;
 633   NameList _operand_inst_num;
 634   NameList _operand_op_name;
 635 
 636 public:
 637 
 638   // Public Methods
 639   PeepReplace(char *rule);
 640   ~PeepReplace();
 641 
 642   // Add contents of peepreplace
 643   void  add_instruction(char *root);
 644   void  add_operand( int inst_num, char *inst_operand );
 645 
 646   // Access contents of peepreplace
 647   void  reset();
 648   void  next_instruction(const char * &root);
 649   void  next_operand(int &inst_num, const char * &inst_operand );
 650 
 651   // Utilities
 652   void dump();
 653   void output(FILE *fp);
 654 };
 655 
 656 
 657 class PeepChild : public Form {
 658 public:
 659   const int   _inst_num;         // Number of instruction (-1 if only named)
 660   const char *_inst_op;          // Instruction's operand, NULL if number == -1
 661   const char *_inst_name;        // Name of the instruction
 662 
 663 public:
 664   PeepChild(char *inst_name)
 665     : _inst_num(-1), _inst_op(NULL), _inst_name(inst_name) {};
 666   PeepChild(int inst_num, char *inst_op, char *inst_name)
 667     : _inst_num(inst_num), _inst_op(inst_op), _inst_name(inst_name) {};
 668   ~PeepChild();
 669 
 670   bool  use_leaf_operand()        { return _inst_num != -1; };
 671   bool  generate_an_instruction() { return _inst_num == -1; }
 672 
 673   void dump();
 674   void output(FILE *fp);
 675 };
 676 
 677 #endif // SHARE_ADLC_FORMSOPT_HPP