%{
/*
 * Copyright © 2018 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "brw_asm_internal.h"

#undef yyerror
#ifdef YYBYACC
struct YYLTYPE;
void yyerror (struct YYLTYPE *, char *);
#else
void yyerror (char *);
#endif

#undef ALIGN16

#define YYLTYPE YYLTYPE
typedef struct YYLTYPE
{
   int first_line;
   int first_column;
   int last_line;
   int last_column;
} YYLTYPE;

enum message_level {
   WARN,
   ERROR,
};

int yydebug = 1;

static void
message(enum message_level level, YYLTYPE *location,
        const char *fmt, ...)
{
   static const char *level_str[] = { "warning", "error" };
   va_list args;

   if (location)
      fprintf(stderr, "%s:%d:%d: %s: ", input_filename,
              location->first_line,
              location->first_column, level_str[level]);
   else
      fprintf(stderr, "%s:%s: ", input_filename, level_str[level]);

   va_start(args, fmt);
   vfprintf(stderr, fmt, args);
   va_end(args);
}

#define warn(flag, l, fmt, ...)                 \
   do {                                         \
      if (warning_flags & WARN_ ## flag)        \
         message(WARN, l, fmt, ## __VA_ARGS__); \
   } while (0)

#define error(l, fmt, ...)                      \
   do {                                         \
      message(ERROR, l, fmt, ## __VA_ARGS__);   \
   } while (0)

static bool
isPowerofTwo(unsigned int x)
{
   return x && (!(x & (x - 1)));
}

static struct brw_reg
set_direct_src_operand(struct brw_reg *reg, int type)
{
   return brw_make_reg(reg->file,
                       reg->nr,
                       reg->subnr,
                       0,      // negate
                       0,      // abs
                       type,
                       0,      // vstride
                       0,      // width
                       0,      // hstride
                       BRW_SWIZZLE_NOOP,
                       WRITEMASK_XYZW);
}

static void
i965_asm_unary_instruction(int opcode, struct brw_codegen *p,
                           struct brw_reg dest, struct brw_reg src0)
{
   switch (opcode) {
   case BRW_OPCODE_BFREV:
      brw_BFREV(p, dest, src0);
      break;
   case BRW_OPCODE_CBIT:
      brw_CBIT(p, dest, src0);
      break;
   case BRW_OPCODE_MOV:
      brw_MOV(p, dest, src0);
      break;
   case BRW_OPCODE_FBL:
      brw_FBL(p, dest, src0);
      break;
   case BRW_OPCODE_FRC:
      brw_FRC(p, dest, src0);
      break;
   case BRW_OPCODE_FBH:
      brw_FBH(p, dest, src0);
      break;
   case BRW_OPCODE_NOT:
      brw_NOT(p, dest, src0);
      break;
   case BRW_OPCODE_RNDE:
      brw_RNDE(p, dest, src0);
      break;
   case BRW_OPCODE_RNDZ:
      brw_RNDZ(p, dest, src0);
      break;
   case BRW_OPCODE_RNDD:
      brw_RNDD(p, dest, src0);
      break;
   case BRW_OPCODE_LZD:
      brw_LZD(p, dest, src0);
      break;
   case BRW_OPCODE_RNDU:
      fprintf(stderr, "Opcode BRW_OPCODE_RNDU unhandled\n");
      break;
   default:
      fprintf(stderr, "Unsupported unary opcode\n");
   }
}

static void
i965_asm_binary_instruction(int opcode,
                            struct brw_codegen *p,
                            struct brw_reg dest,
                            struct brw_reg src0,
                            struct brw_reg src1)
{
   switch (opcode) {
   case BRW_OPCODE_ADDC:
      brw_ADDC(p, dest, src0, src1);
      break;
   case BRW_OPCODE_BFI1:
      brw_BFI1(p, dest, src0, src1);
      break;
   case BRW_OPCODE_DP2:
      brw_DP2(p, dest, src0, src1);
      break;
   case BRW_OPCODE_DP3:
      brw_DP3(p, dest, src0, src1);
      break;
   case BRW_OPCODE_DP4:
      brw_DP4(p, dest, src0, src1);
      break;
   case BRW_OPCODE_DPH:
      brw_DPH(p, dest, src0, src1);
      break;
   case BRW_OPCODE_LINE:
      brw_LINE(p, dest, src0, src1);
      break;
   case BRW_OPCODE_MAC:
      brw_MAC(p, dest, src0, src1);
      break;
   case BRW_OPCODE_MACH:
      brw_MACH(p, dest, src0, src1);
      break;
   case BRW_OPCODE_PLN:
      brw_PLN(p, dest, src0, src1);
      break;
   case BRW_OPCODE_ROL:
      brw_ROL(p, dest, src0, src1);
      break;
   case BRW_OPCODE_ROR:
      brw_ROR(p, dest, src0, src1);
      break;
   case BRW_OPCODE_SUBB:
      brw_SUBB(p, dest, src0, src1);
      break;
   case BRW_OPCODE_ADD:
      brw_ADD(p, dest, src0, src1);
      break;
   case BRW_OPCODE_CMP:
      /* Third parameter is conditional modifier
       * which gets updated later
       */
      brw_CMP(p, dest, 0, src0, src1);
      break;
   case BRW_OPCODE_AND:
      brw_AND(p, dest, src0, src1);
      break;
   case BRW_OPCODE_ASR:
      brw_ASR(p, dest, src0, src1);
      break;
   case BRW_OPCODE_AVG:
      brw_AVG(p, dest, src0, src1);
      break;
   case BRW_OPCODE_OR:
      brw_OR(p, dest, src0, src1);
      break;
   case BRW_OPCODE_SEL:
      brw_SEL(p, dest, src0, src1);
      break;
   case BRW_OPCODE_SHL:
      brw_SHL(p, dest, src0, src1);
      break;
   case BRW_OPCODE_SHR:
      brw_SHR(p, dest, src0, src1);
      break;
   case BRW_OPCODE_XOR:
      brw_XOR(p, dest, src0, src1);
      break;
   case BRW_OPCODE_MUL:
      brw_MUL(p, dest, src0, src1);
      break;
   default:
      fprintf(stderr, "Unsupported binary opcode\n");
   }
}

static void
i965_asm_ternary_instruction(int opcode,
                             struct brw_codegen *p,
                             struct brw_reg dest,
                             struct brw_reg src0,
                             struct brw_reg src1,
                             struct brw_reg src2)
{
   switch (opcode) {
   case BRW_OPCODE_MAD:
      brw_MAD(p, dest, src0, src1, src2);
      break;
   case BRW_OPCODE_CSEL:
      brw_CSEL(p, dest, src0, src1, src2);
      break;
   case BRW_OPCODE_LRP:
      brw_LRP(p, dest, src0, src1, src2);
      break;
   case BRW_OPCODE_BFE:
      brw_BFE(p, dest, src0, src1, src2);
      break;
   case BRW_OPCODE_BFI2:
      brw_BFI2(p, dest, src0, src1, src2);
      break;
   case BRW_OPCODE_DP4A:
      brw_DP4A(p, dest, src0, src1, src2);
      break;
   case BRW_OPCODE_ADD3:
      brw_ADD3(p, dest, src0, src1, src2);
      break;
   default:
      fprintf(stderr, "Unsupported ternary opcode\n");
   }
}

static void
i965_asm_set_instruction_options(struct brw_codegen *p,
                                 struct options options)
{
   brw_eu_inst_set_access_mode(p->devinfo, brw_last_inst,
                               options.access_mode);
   brw_eu_inst_set_mask_control(p->devinfo, brw_last_inst,
                                options.mask_control);
   if (p->devinfo->ver < 12) {
      brw_eu_inst_set_thread_control(p->devinfo, brw_last_inst,
                                     options.thread_control);
      brw_eu_inst_set_no_dd_check(p->devinfo, brw_last_inst,
                                  options.no_dd_check);
      brw_eu_inst_set_no_dd_clear(p->devinfo, brw_last_inst,
                                  options.no_dd_clear);
   } else {
      enum opcode opcode = brw_eu_inst_opcode(p->isa, brw_last_inst);
      brw_eu_inst_set_swsb(p->devinfo, brw_last_inst,
                           tgl_swsb_encode(p->devinfo, options.depinfo, opcode));
   }
   brw_eu_inst_set_debug_control(p->devinfo, brw_last_inst,
                                 options.debug_control);
   if (brw_has_branch_ctrl(p->devinfo, brw_eu_inst_opcode(p->isa, brw_last_inst))) {
      if (options.acc_wr_control)
         error(NULL, "Instruction does not support AccWrEnable\n");

      brw_eu_inst_set_branch_control(p->devinfo, brw_last_inst,
                                     options.branch_control);
   } else if (options.branch_control) {
      error(NULL, "Instruction does not support BranchCtrl\n");
   } else if (p->devinfo->ver < 20) {
      brw_eu_inst_set_acc_wr_control(p->devinfo, brw_last_inst,
                                     options.acc_wr_control);
   }
   brw_eu_inst_set_cmpt_control(p->devinfo, brw_last_inst,
                                options.compaction);
}

%}

%locations

%start ROOT

%union {
   char *string;
   double number;
   int integer;
   unsigned long long int llint;
   struct brw_reg reg;
   enum brw_reg_type reg_type;
   struct brw_codegen *program;
   struct predicate predicate;
   struct condition condition;
   struct options options;
   struct instoption instoption;
   struct msgdesc msgdesc;
   struct tgl_swsb depinfo;
   struct { int sdepth; int rcount; } dpas_params;
   brw_eu_inst *instruction;
}

%token ABS
%token COLON
%token COMMA
%token DOT
%token LANGLE RANGLE
%token LCURLY RCURLY
%token LPAREN RPAREN
%token LSQUARE RSQUARE
%token PLUS MINUS
%token SEMICOLON
%token ASSIGN

/* datatypes */
%token <integer> TYPE_B TYPE_UB
%token <integer> TYPE_W TYPE_UW
%token <integer> TYPE_D TYPE_UD
%token <integer> TYPE_Q TYPE_UQ
%token <integer> TYPE_V TYPE_UV
%token <integer> TYPE_F TYPE_HF
%token <integer> TYPE_BF
%token <integer> TYPE_DF
%token <integer> TYPE_VF

/* label */
%token <string> JUMP_LABEL
%token <string> JUMP_LABEL_TARGET
%token JIP UIP

/* opcodes */
%token <integer> ADD ADD3 ADDC AND ASR AVG
%token <integer> BFE BFI1 BFI2 BFB BFREV BRC BRD BREAK
%token <integer> CALL CALLA CASE CBIT CMP CMPN CONT CSEL
%token <integer> DIM DO DPAS DP2 DP3 DP4 DP4A DPH
%token <integer> ELSE ENDIF FBH FBL FORK FRC
%token <integer> GOTO
%token <integer> HALT
%token <integer> IF ILLEGAL
%token <integer> JMPI JOIN
%token <integer> LINE LRP LZD
%token <integer> MAC MACH MAD MADM MATH MOV MOVI MUL MREST MSAVE
%token <integer> NENOP NOP NOT
%token <integer> OR
%token <integer> PLN POP PUSH
%token <integer> RET RNDD RNDE RNDU RNDZ ROL ROR
%token <integer> SEL SENDS SENDSC SHL SHR SMOV SUBB SYNC
%token <integer> SEND_GFX4 SENDC_GFX4 SEND_GFX12 SENDC_GFX12
%token <integer> WAIT WHILE
%token <integer> XOR

/* extended math functions */
%token <integer> COS EXP FDIV INV INVM INTDIV INTDIVMOD INTMOD LOG POW RSQ
%token <integer> RSQRTM SIN SINCOS SQRT

/* sync instruction */
%token <integer> ALLRD ALLWR FENCE BAR HOST
%type <integer> sync_function
%type <reg> sync_arg

/* shared functions for send */
%token HDC0 HDC1 HDC2 HDC_RO GATEWAY PIXEL_INTERP RENDER SAMPLER
%token TS_BTD URB RT_ACCEL SLM TGM UGM

/* message details for send */
%token MSGDESC_BEGIN SRC1_LEN EX_BSO MSGDESC_END
%type <msgdesc> msgdesc msgdesc_parts;

/* Conditional modifiers */
%token <integer> EQUAL GREATER GREATER_EQUAL LESS LESS_EQUAL NOT_EQUAL
%token <integer> NOT_ZERO OVERFLOW UNORDERED ZERO

/* register Access Modes */
%token ALIGN1 ALIGN16

/* accumulator write control */
%token ACCWREN

/* compaction control */
%token CMPTCTRL

/* mask control (WeCtrl) */
%token WECTRL

/* debug control */
%token BREAKPOINT

/* dependency control */
%token NODDCLR NODDCHK

/* end of thread */
%token EOT

/* mask control */
%token MASK_DISABLE;

/* predicate control */
%token <integer> ANYV ALLV ANY2H ALL2H ANY4H ALL4H ANY8H ALL8H ANY16H ALL16H
%token <integer> ANY32H ALL32H

/* round instructions */
%token <integer> ROUND_INCREMENT

/* staturation */
%token SATURATE

/* thread control */
%token ATOMIC SWITCH

/* branch control */
%token BRANCH_CTRL

/* quater control */
%token QTR_2Q QTR_3Q QTR_4Q QTR_2H QTR_2N QTR_3N QTR_4N QTR_5N
%token QTR_6N QTR_7N QTR_8N

/* channels */
%token <integer> X Y Z W

/* reg files */
%token GENREGFILE

/* vertical stride in register region */
%token VxH

/* register type */
%token <integer> GENREG ADDRREG ACCREG FLAGREG NOTIFYREG STATEREG
%token <integer> CONTROLREG IPREG PERFORMANCEREG THREADREG CHANNELENABLEREG
%token <integer> MASKREG SCALARREG

%token <integer> INTEGER
%token <llint> LONG
%token NULL_TOKEN

%nonassoc SUBREGNUM
%left PLUS MINUS
%nonassoc DOT
%nonassoc EMPTYEXECSIZE
%nonassoc LPAREN

%type <integer> execsize exp
%type <llint> exp2

/* predicate control */
%type <integer> predctrl predstate
%type <predicate> predicate

/* conditional modifier */
%type <condition> cond_mod
%type <integer> condModifiers

/* instruction options  */
%type <options> instoptions instoption_list
%type <instoption> instoption

/* writemask */
%type <integer> writemask_x writemask_y writemask_z writemask_w
%type <integer> writemask

/* dst operand */
%type <reg> dst dstoperand dstoperandex dstoperandex_typed dstreg
%type <integer> dstregion

%type <integer> saturate
%type <reg> relativelocation2

/* src operand */
%type <reg> directsrcoperand directsrcaccoperand indirectsrcoperand srcacc
%type <reg> srcarcoperandex srcaccimm srcarcoperandex_typed srcimm
%type <reg> indirectgenreg indirectregion
%type <reg> immreg src reg32 payload directgenreg_list addrparam region
%type <reg> region_wh directgenreg
%type <reg> desc ex_desc reg32a
%type <integer> swizzle

/* registers */
%type <reg> accreg addrreg channelenablereg controlreg flagreg ipreg scalarreg
%type <reg> notifyreg nullreg performancereg threadcontrolreg statereg maskreg
%type <integer> subregnum

/* register types */
%type <reg_type> reg_type imm_type

/* immediate values */
%type <llint> immval

/* instruction opcodes */
%type <integer> unaryopcodes binaryopcodes binaryaccopcodes ternaryopcodes
%type <integer> sendop sendsop
%type <instruction> sendopcode sendsopcode

%type <integer> negate abs chansel math_function sharedfunction

%type <string> jumplabeltarget

/* SWSB */
%token <integer> REG_DIST_CURRENT
%token <integer> REG_DIST_FLOAT
%token <integer> REG_DIST_INT
%token <integer> REG_DIST_LONG
%token <integer> REG_DIST_ALL
%token <integer> REG_DIST_MATH
%token <integer> REG_DIST_SCALAR
%token <integer> SBID_ALLOC
%token <integer> SBID_WAIT_SRC
%token <integer> SBID_WAIT_DST

%type <depinfo> depinfo

/* DPAS */
%token <dpas_params> DPAS_PARAMS

%code {

static void
add_instruction_option(struct options *options, struct instoption opt)
{
   if (opt.type == INSTOPTION_DEP_INFO) {
      if (opt.depinfo_value.regdist) {
         options->depinfo.regdist = opt.depinfo_value.regdist;
         options->depinfo.pipe = opt.depinfo_value.pipe;
      } else {
         options->depinfo.sbid = opt.depinfo_value.sbid;
         options->depinfo.mode = opt.depinfo_value.mode;
      }
      return;
   }
   if (opt.type == INSTOPTION_CHAN_OFFSET) {
      options->chan_offset = opt.uint_value;
      return;
   }
   switch (opt.uint_value) {
   case ALIGN1:
      options->access_mode = BRW_ALIGN_1;
      break;
   case ALIGN16:
      options->access_mode = BRW_ALIGN_16;
      break;
   case SWITCH:
      options->thread_control |= BRW_THREAD_SWITCH;
      break;
   case ATOMIC:
      options->thread_control |= BRW_THREAD_ATOMIC;
      break;
   case BRANCH_CTRL:
      options->branch_control = true;
      break;
   case NODDCHK:
      options->no_dd_check = true;
      break;
   case NODDCLR:
      options->no_dd_clear = true;
      break;
   case MASK_DISABLE:
      options->mask_control |= BRW_MASK_DISABLE;
      break;
   case BREAKPOINT:
      options->debug_control = BRW_DEBUG_BREAKPOINT;
      break;
   case WECTRL:
      options->mask_control |= BRW_WE_ALL;
      break;
   case CMPTCTRL:
      /* Don't set the compaction flag to true, we're just reading
       * text assembly, not instruction bits. The code that will
       * assemble things later will set the flag if it decides to
       * compact instructions.
       */
      if (!compaction_warning_given) {
         compaction_warning_given = true;
         fprintf(stderr, "%s: ignoring 'compacted' "
                 "annotations for text assembly "
                 "instructions\n", input_filename);
      }
      break;
   case ACCWREN:
      options->acc_wr_control = true;
      break;
   case EOT:
      options->end_of_thread = true;
      break;
   }
}
}
%%

ROOT:
   instrseq
   ;

instrseq:
   instrseq instruction SEMICOLON
   | instrseq relocatableinstruction SEMICOLON
   | instruction SEMICOLON
   | relocatableinstruction SEMICOLON
   | instrseq jumplabeltarget
   | jumplabeltarget
   ;

/* Instruction Group */
instruction:
   unaryinstruction
   | binaryinstruction
   | binaryaccinstruction
   | mathinstruction
   | nopinstruction
   | waitinstruction
   | ternaryinstruction
   | sendinstruction
   | illegalinstruction
   | syncinstruction
   ;

relocatableinstruction:
   jumpinstruction
   | branchinstruction
   | breakinstruction
   | loopinstruction
   | joininstruction
   ;

illegalinstruction:
   ILLEGAL execsize instoptions
   {
      brw_next_insn(p, $1);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $2);
      i965_asm_set_instruction_options(p, $3);
   }
   ;

/* Unary instruction */
unaryinstruction:
   predicate unaryopcodes saturate cond_mod execsize dst srcaccimm   instoptions
   {
      brw_set_default_access_mode(p, $8.access_mode);
      i965_asm_unary_instruction($2, p, $6, $7);
      brw_pop_insn_state(p);
      i965_asm_set_instruction_options(p, $8);
      if ($4.cond_modifier) {
         brw_eu_inst_set_cond_modifier(p->devinfo,
                                       brw_last_inst,
                                       $4.cond_modifier);
      }

      if (!brw_eu_inst_flag_reg_nr(p->devinfo, brw_last_inst)) {
         brw_eu_inst_set_flag_reg_nr(p->devinfo,
                                     brw_last_inst,
                                     $4.flag_reg_nr);
         brw_eu_inst_set_flag_subreg_nr(p->devinfo,
                                        brw_last_inst,
                                        $4.flag_subreg_nr);
      }

      if ($7.file != IMM) {
         brw_eu_inst_set_src0_vstride(p->devinfo, brw_last_inst,
                                      $7.vstride);
      }
      brw_eu_inst_set_saturate(p->devinfo, brw_last_inst, $3);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $5);
      brw_eu_inst_set_group(p->devinfo, brw_last_inst, $8.chan_offset);
   }
   ;

unaryopcodes:
   BFREV
   | CBIT
   | DIM
   | FBH
   | FBL
   | FRC
   | LZD
   | MOV
   | NOT
   | RNDD
   | RNDE
   | RNDU
   | RNDZ
   ;

/* Binary instruction */
binaryinstruction:
   predicate binaryopcodes saturate cond_mod execsize dst srcimm srcimm instoptions
   {
      brw_set_default_access_mode(p, $9.access_mode);
      i965_asm_binary_instruction($2, p, $6, $7, $8);
      i965_asm_set_instruction_options(p, $9);
      if ($4.cond_modifier) {
         brw_eu_inst_set_cond_modifier(p->devinfo,
                                       brw_last_inst,
                                       $4.cond_modifier);
      }

      if (!brw_eu_inst_flag_reg_nr(p->devinfo, brw_last_inst)) {
         brw_eu_inst_set_flag_reg_nr(p->devinfo, brw_last_inst,
                                     $4.flag_reg_nr);
         brw_eu_inst_set_flag_subreg_nr(p->devinfo, brw_last_inst,
                                        $4.flag_subreg_nr);
      }

      brw_eu_inst_set_saturate(p->devinfo, brw_last_inst, $3);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $5);
      brw_eu_inst_set_group(p->devinfo, brw_last_inst, $9.chan_offset);

      brw_pop_insn_state(p);
   }
   ;

binaryopcodes:
   ADDC
   | BFI1
   | DP2
   | DP3
   | DP4
   | DPH
   | LINE
   | MAC
   | MACH
   | MUL
   | PLN
   | ROL
   | ROR
   | SUBB
   ;

/* Binary acc instruction */
binaryaccinstruction:
   predicate binaryaccopcodes saturate cond_mod execsize dst srcacc srcimm instoptions
   {
      brw_set_default_access_mode(p, $9.access_mode);
      i965_asm_binary_instruction($2, p, $6, $7, $8);
      brw_pop_insn_state(p);
      i965_asm_set_instruction_options(p, $9);
      if ($4.cond_modifier) {
         brw_eu_inst_set_cond_modifier(p->devinfo,
                                       brw_last_inst,
                                       $4.cond_modifier);
      }

      if (!brw_eu_inst_flag_reg_nr(p->devinfo, brw_last_inst)) {
         brw_eu_inst_set_flag_reg_nr(p->devinfo,
                                     brw_last_inst,
                                     $4.flag_reg_nr);
         brw_eu_inst_set_flag_subreg_nr(p->devinfo,
                                        brw_last_inst,
                                        $4.flag_subreg_nr);
      }

      brw_eu_inst_set_saturate(p->devinfo, brw_last_inst, $3);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $5);
      brw_eu_inst_set_group(p->devinfo, brw_last_inst, $9.chan_offset);
   }
   ;

binaryaccopcodes:
   ADD
   | AND
   | ASR
   | AVG
   | CMP
   | CMPN
   | OR
   | SEL
   | SHL
   | SHR
   | XOR
   ;

/* Math instruction */
mathinstruction:
   predicate MATH saturate math_function execsize dst src srcimm instoptions
   {
      brw_set_default_access_mode(p, $9.access_mode);
      gfx6_math(p, $6, $4, $7, $8);
      i965_asm_set_instruction_options(p, $9);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $5);
      brw_eu_inst_set_saturate(p->devinfo, brw_last_inst, $3);
      brw_eu_inst_set_group(p->devinfo, brw_last_inst, $9.chan_offset);
      brw_pop_insn_state(p);
   }
   ;

math_function:
   COS
   | EXP
   | FDIV
   | INV
   | INVM
   | INTDIV
   | INTDIVMOD
   | INTMOD
   | LOG
   | POW
   | RSQ
   | RSQRTM
   | SIN
   | SQRT
   | SINCOS
   ;

/* NOP instruction */
nopinstruction:
   NOP
   {
      brw_NOP(p);
   }
   ;

/* Ternary operand instruction */
ternaryinstruction:
   predicate ternaryopcodes saturate cond_mod execsize dst srcimm src srcimm instoptions
   {
      brw_set_default_access_mode(p, $10.access_mode);
      i965_asm_ternary_instruction($2, p, $6, $7, $8, $9);
      brw_pop_insn_state(p);
      i965_asm_set_instruction_options(p, $10);
      if ($4.cond_modifier) {
         brw_eu_inst_set_cond_modifier(p->devinfo,
                                       brw_last_inst,
                                       $4.cond_modifier);
      }

      if (p->devinfo->ver < 12) {
         brw_eu_inst_set_3src_a16_flag_reg_nr(p->devinfo, brw_last_inst,
                                              $4.flag_reg_nr);
         brw_eu_inst_set_3src_a16_flag_subreg_nr(p->devinfo, brw_last_inst,
                                                 $4.flag_subreg_nr);
      }

      brw_eu_inst_set_saturate(p->devinfo, brw_last_inst, $3);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $5);
      brw_eu_inst_set_group(p->devinfo, brw_last_inst, $10.chan_offset);
   }
   |
   predicate DPAS DPAS_PARAMS saturate cond_mod execsize dst src src src instoptions
   {
      assert(p->devinfo->verx10 >= 125);

      brw_set_default_access_mode(p, $11.access_mode);

      brw_DPAS(p, translate_systolic_depth($3.sdepth), $3.rcount, $7, $8, $9, $10);
      brw_pop_insn_state(p);
      i965_asm_set_instruction_options(p, $11);
      if ($5.cond_modifier) {
         brw_eu_inst_set_cond_modifier(p->devinfo,
                                       brw_last_inst,
                                       $5.cond_modifier);
      }

      brw_eu_inst_set_saturate(p->devinfo, brw_last_inst, $4);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $6);
      brw_eu_inst_set_group(p->devinfo, brw_last_inst, $11.chan_offset);
   }
   ;

ternaryopcodes:
   CSEL
   | BFE
   | BFI2
   | LRP
   | MAD
   | DP4A
   | ADD3
   ;

/* Wait instruction */
waitinstruction:
   WAIT execsize dst instoptions
   {
      brw_next_insn(p, $1);
      i965_asm_set_instruction_options(p, $4);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $2);
      brw_set_default_access_mode(p, $4.access_mode);
      struct brw_reg dest = $3;
      dest.swizzle = brw_swizzle_for_mask(dest.writemask);
      if (dest.file != ARF || dest.nr != BRW_ARF_NOTIFICATION_COUNT)
         error(&@1, "WAIT must use the notification register\n");
      brw_set_dest(p, brw_last_inst, dest);
      brw_set_src0(p, brw_last_inst, dest);
      brw_set_src1(p, brw_last_inst, brw_null_reg());
      brw_eu_inst_set_mask_control(p->devinfo, brw_last_inst, BRW_MASK_DISABLE);
   }
   ;

/* Send instruction */
sendinstruction:
   predicate sendopcode execsize dst payload exp2 sharedfunction msgdesc instoptions
   {
      i965_asm_set_instruction_options(p, $9);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);
      brw_set_dest(p, brw_last_inst, $4);
      brw_set_src0(p, brw_last_inst, $5);
      brw_eu_inst_set_bits(brw_last_inst, 127, 96, $6);
      brw_eu_inst_set_src1_file_type(p->devinfo, brw_last_inst,
                                     IMM, BRW_TYPE_UD);
      brw_eu_inst_set_sfid(p->devinfo, brw_last_inst, $7);
      brw_eu_inst_set_eot(p->devinfo, brw_last_inst, $9.end_of_thread);
      brw_eu_inst_set_group(p->devinfo, brw_last_inst, $9.chan_offset);

      brw_pop_insn_state(p);
   }
   | predicate sendopcode execsize dst payload payload exp2 sharedfunction msgdesc instoptions
   {
      assert(p->devinfo->ver < 12);

      i965_asm_set_instruction_options(p, $10);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);
      brw_set_dest(p, brw_last_inst, $4);
      brw_set_src0(p, brw_last_inst, $5);
      brw_eu_inst_set_bits(brw_last_inst, 127, 96, $7);
      brw_eu_inst_set_sfid(p->devinfo, brw_last_inst, $8);
      brw_eu_inst_set_eot(p->devinfo, brw_last_inst, $10.end_of_thread);
      brw_eu_inst_set_group(p->devinfo, brw_last_inst, $10.chan_offset);

      brw_pop_insn_state(p);
   }
   | predicate sendsopcode execsize dst payload payload desc ex_desc sharedfunction msgdesc instoptions
   {
      i965_asm_set_instruction_options(p, $11);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);
      brw_set_dest(p, brw_last_inst, $4);
      brw_set_src0(p, brw_last_inst, $5);
      brw_set_src1(p, brw_last_inst, $6);

      if ($7.file == IMM) {
         brw_eu_inst_set_send_sel_reg32_desc(p->devinfo, brw_last_inst, 0);
         brw_eu_inst_set_send_desc(p->devinfo, brw_last_inst, $7.ud);
      } else {
         brw_eu_inst_set_send_sel_reg32_desc(p->devinfo, brw_last_inst, 1);
      }

      if ($8.file == IMM) {
         brw_eu_inst_set_send_sel_reg32_ex_desc(p->devinfo, brw_last_inst, 0);
         brw_eu_inst_set_sends_ex_desc(p->devinfo, brw_last_inst, $8.ud, false);
      } else {
         brw_eu_inst_set_send_sel_reg32_ex_desc(p->devinfo, brw_last_inst, 1);
         brw_eu_inst_set_send_ex_desc_ia_subreg_nr(p->devinfo, brw_last_inst, $8.subnr >> 2);
      }

      brw_eu_inst_set_sfid(p->devinfo, brw_last_inst, $9);
      brw_eu_inst_set_eot(p->devinfo, brw_last_inst, $11.end_of_thread);
      brw_eu_inst_set_group(p->devinfo, brw_last_inst, $11.chan_offset);

      if (p->devinfo->verx10 >= 125 && $10.ex_bso) {
         brw_eu_inst_set_send_ex_bso(p->devinfo, brw_last_inst, 1);
         brw_eu_inst_set_send_src1_len(p->devinfo, brw_last_inst, $10.src1_len);
      }

      brw_pop_insn_state(p);
   }
   | predicate sendsopcode execsize dst GENREGFILE LSQUARE scalarreg RSQUARE desc ex_desc sharedfunction msgdesc instoptions
   {
                assert(p->devinfo->ver >= 30);

      i965_asm_set_instruction_options(p, $13);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);
      brw_set_dest(p, brw_last_inst, $4);
      brw_set_src0(p, brw_last_inst, $7);
      brw_set_src1(p, brw_last_inst, brw_null_reg());

      if ($9.file == IMM) {
         brw_eu_inst_set_send_sel_reg32_desc(p->devinfo, brw_last_inst, 0);
         brw_eu_inst_set_send_desc(p->devinfo, brw_last_inst, $9.ud);
      } else {
         brw_eu_inst_set_send_sel_reg32_desc(p->devinfo, brw_last_inst, 1);
      }

      if ($10.file == IMM) {
         brw_eu_inst_set_send_sel_reg32_ex_desc(p->devinfo, brw_last_inst, 0);
         brw_eu_inst_set_sends_ex_desc(p->devinfo, brw_last_inst, $10.ud, true);
      } else {
         brw_eu_inst_set_send_sel_reg32_ex_desc(p->devinfo, brw_last_inst, 1);
         brw_eu_inst_set_send_ex_desc_ia_subreg_nr(p->devinfo, brw_last_inst, $10.subnr >> 2);
      }

      brw_eu_inst_set_sfid(p->devinfo, brw_last_inst, $11);
      brw_eu_inst_set_eot(p->devinfo, brw_last_inst, $13.end_of_thread);
      brw_eu_inst_set_group(p->devinfo, brw_last_inst, $13.chan_offset);

      if ($12.ex_bso) {
         brw_eu_inst_set_send_ex_bso(p->devinfo, brw_last_inst, 1);
         /* Not settings src1 length, as its implied zero. */
      }

      brw_pop_insn_state(p);
   }
   ;

sendop:
   SEND_GFX4
   | SENDC_GFX4
   ;

sendsop:
   SEND_GFX12
   | SENDC_GFX12
   | SENDS
   | SENDSC
   ;

sendopcode:
   sendop   { $$ = brw_next_insn(p, $1); }
   ;

sendsopcode:
   sendsop  { $$ = brw_next_insn(p, $1); }
   ;

sharedfunction:
   NULL_TOKEN          { $$ = BRW_SFID_NULL; }
   | GATEWAY           { $$ = BRW_SFID_MESSAGE_GATEWAY; }
   | URB               { $$ = BRW_SFID_URB; }
   | TS_BTD            { $$ = BRW_SFID_BINDLESS_THREAD_DISPATCH; }
   | RENDER            { $$ = BRW_SFID_RENDER_CACHE; }
   | HDC_RO            { $$ = BRW_SFID_HDC_READ_ONLY; }
   | HDC0              { $$ = BRW_SFID_HDC0; }
   | PIXEL_INTERP      { $$ = BRW_SFID_PIXEL_INTERPOLATOR; }
   | HDC1              { $$ = BRW_SFID_HDC1; }
   | SAMPLER           { $$ = BRW_SFID_SAMPLER; }
   | HDC2              { $$ = BRW_SFID_HDC2; }
   | RT_ACCEL          { $$ = BRW_SFID_RAY_TRACE_ACCELERATOR; }
   | SLM               { $$ = BRW_SFID_SLM; }
   | TGM               { $$ = BRW_SFID_TGM; }
   | UGM               { $$ = BRW_SFID_UGM; }
   ;

exp2:
   LONG       { $$ = $1; }
   | MINUS LONG    { $$ = -$2; }
   ;

desc:
   reg32a
   | exp2
   {
      $$ = brw_imm_ud($1);
   }
   ;

ex_desc:
   reg32a
   | exp2
   {
      $$ = brw_imm_ud($1);
   }
   ;

reg32a:
   addrreg region reg_type
   {
      $$ = set_direct_src_operand(&$1, $3);
      $$ = stride($$, $2.vstride, $2.width, $2.hstride);
   }
   ;


/* Jump instruction */
jumpinstruction:
   predicate JMPI execsize relativelocation2 instoptions
   {
      brw_next_insn(p, $2);
      i965_asm_set_instruction_options(p, $5);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);
      brw_set_dest(p, brw_last_inst, brw_ip_reg());
      brw_set_src0(p, brw_last_inst, brw_ip_reg());
      brw_set_src1(p, brw_last_inst, $4);
      brw_eu_inst_set_pred_control(p->devinfo, brw_last_inst,
                                   brw_eu_inst_pred_control(p->devinfo, brw_last_inst));
      brw_pop_insn_state(p);
   }
   ;

/* branch instruction */
branchinstruction:
   predicate ENDIF execsize JIP JUMP_LABEL instoptions
   {
      brw_next_insn(p, $2);
      brw_asm_label_use_jip($5);
      i965_asm_set_instruction_options(p, $6);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);

      brw_set_src0(p, brw_last_inst, brw_imm_d(0x0));

      brw_pop_insn_state(p);
   }
   | ELSE execsize JIP JUMP_LABEL UIP JUMP_LABEL instoptions
   {
      brw_next_insn(p, $1);
      brw_asm_label_use_jip($4);
      brw_asm_label_use_uip($6);
      i965_asm_set_instruction_options(p, $7);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $2);

      brw_set_dest(p, brw_last_inst, retype(brw_null_reg(), BRW_TYPE_D));
      if (p->devinfo->ver < 12)
         brw_set_src0(p, brw_last_inst, brw_imm_d(0));
   }
   | predicate IF execsize JIP JUMP_LABEL UIP JUMP_LABEL instoptions
   {
      brw_next_insn(p, $2);
      i965_asm_set_instruction_options(p, $8);
      brw_asm_label_use_jip($5);
      brw_asm_label_use_uip($7);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);

      brw_set_dest(p, brw_last_inst, vec1(retype(brw_null_reg(), BRW_TYPE_D)));
      if (p->devinfo->ver < 12)
         brw_set_src0(p, brw_last_inst, brw_imm_d(0x0));

      brw_pop_insn_state(p);
   }
   | predicate GOTO execsize JIP JUMP_LABEL UIP JUMP_LABEL instoptions
   {
      brw_next_insn(p, $2);
      brw_asm_label_use_jip($5);
      brw_asm_label_use_uip($7);
      i965_asm_set_instruction_options(p, $8);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);

      brw_pop_insn_state(p);
   }
   ;

joininstruction:
   predicate JOIN execsize JIP JUMP_LABEL instoptions
   {
      brw_next_insn(p, $2);
      brw_asm_label_use_jip($5);
      i965_asm_set_instruction_options(p, $6);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);

      brw_pop_insn_state(p);
   }
   ;

/* break instruction */
breakinstruction:
   predicate BREAK execsize JIP JUMP_LABEL UIP JUMP_LABEL instoptions
   {

      brw_next_insn(p, $2);
      brw_asm_label_use_jip($5);
      brw_asm_label_use_uip($7);
      i965_asm_set_instruction_options(p, $8);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);

      brw_set_dest(p, brw_last_inst, retype(brw_null_reg(), BRW_TYPE_D));
      brw_set_src0(p, brw_last_inst, brw_imm_d(0x0));

      brw_pop_insn_state(p);
   }
   | predicate HALT execsize JIP JUMP_LABEL UIP JUMP_LABEL instoptions
   {
      brw_next_insn(p, $2);
      brw_asm_label_use_jip($5);
      brw_asm_label_use_uip($7);
      i965_asm_set_instruction_options(p, $8);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);

      brw_set_dest(p, brw_last_inst, retype(brw_null_reg(), BRW_TYPE_D));

      if (p->devinfo->ver < 12) {
         brw_set_src0(p, brw_last_inst, brw_imm_d(0x0));
      }

      brw_pop_insn_state(p);
   }
   | predicate CONT execsize JIP JUMP_LABEL UIP JUMP_LABEL instoptions
   {
      brw_next_insn(p, $2);
      brw_asm_label_use_jip($5);
      brw_asm_label_use_uip($7);
      i965_asm_set_instruction_options(p, $8);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);
      brw_set_dest(p, brw_last_inst, brw_ip_reg());

      brw_set_src0(p, brw_last_inst, brw_imm_d(0x0));

      brw_pop_insn_state(p);
   }
   ;

/* loop instruction */
loopinstruction:
   predicate WHILE execsize JIP JUMP_LABEL instoptions
   {
      brw_next_insn(p, $2);
      brw_asm_label_use_jip($5);
      i965_asm_set_instruction_options(p, $6);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $3);

      brw_set_dest(p, brw_last_inst, retype(brw_null_reg(), BRW_TYPE_D));
      if (p->devinfo->ver < 12)
         brw_set_src0(p, brw_last_inst, brw_imm_d(0x0));

      brw_pop_insn_state(p);
   }
   | DO execsize instoptions
   {
      brw_next_insn(p, $1);
   }
   ;

/* sync instruction */
syncinstruction:
   predicate SYNC sync_function execsize sync_arg instoptions
   {
      if (p->devinfo->ver < 12) {
         error(&@2, "sync instruction is supported only on gfx12+\n");
      }

      if ($5.file == IMM &&
          $3 != TGL_SYNC_ALLRD &&
          $3 != TGL_SYNC_ALLWR) {
         error(&@2, "Only allrd and allwr support immediate argument\n");
      }

      brw_set_default_access_mode(p, $6.access_mode);
      brw_SYNC(p, $3);
      i965_asm_set_instruction_options(p, $6);
      brw_eu_inst_set_exec_size(p->devinfo, brw_last_inst, $4);
      brw_set_src0(p, brw_last_inst, $5);
      brw_eu_inst_set_eot(p->devinfo, brw_last_inst, $6.end_of_thread);
      brw_eu_inst_set_group(p->devinfo, brw_last_inst, $6.chan_offset);

      brw_pop_insn_state(p);
   }
   ;

sync_function:
   NOP      { $$ = TGL_SYNC_NOP; }
   | ALLRD
   | ALLWR
   | FENCE
   | BAR
   | HOST
   ;

sync_arg:
   nullreg region reg_type
   {
      $$ = $1;
      $$.vstride = $2.vstride;
      $$.width = $2.width;
      $$.hstride = $2.hstride;
      $$.type = $3;
   }
   | immreg
   ;

/* Relative location */
relativelocation2:
   immreg
   | reg32
   ;

jumplabeltarget:
   JUMP_LABEL_TARGET
   {
      brw_asm_label_set($1);
   }
   ;

/* Destination register */
dst:
   dstoperand
   | dstoperandex
   ;

dstoperand:
   dstreg dstregion writemask reg_type
   {
      $$ = $1;
      $$.vstride = BRW_VERTICAL_STRIDE_1;
      $$.width = BRW_WIDTH_1;
      $$.hstride = $2;
      $$.type = $4;
      $$.writemask = $3;
      $$.swizzle = BRW_SWIZZLE_NOOP;
      $$.subnr = $$.subnr * brw_type_size_bytes($4);
   }
   ;

dstoperandex:
   dstoperandex_typed dstregion writemask reg_type
   {
      $$ = $1;
      $$.hstride = $2;
      $$.type = $4;
      $$.writemask = $3;
      $$.subnr = $$.subnr * brw_type_size_bytes($4);
   }
   /* BSpec says "When the conditional modifier is present, updates
    * to the selected flag register also occur. In this case, the
    * register region fields of the ‘null’ operand are valid."
    */
   | nullreg dstregion writemask reg_type
   {
      $$ = $1;
      $$.vstride = BRW_VERTICAL_STRIDE_1;
      $$.width = BRW_WIDTH_1;
      $$.hstride = $2;
      $$.writemask = $3;
      $$.type = $4;
   }
   | threadcontrolreg
   {
      $$ = $1;
      $$.hstride = 1;
      $$.type = BRW_TYPE_UW;
   }
   ;

dstoperandex_typed:
   accreg
   | addrreg
   | channelenablereg
   | controlreg
   | flagreg
   | ipreg
   | maskreg
   | notifyreg
   | performancereg
   | statereg
   | scalarreg
   ;

dstreg:
   directgenreg
   {
      $$ = $1;
      $$.address_mode = BRW_ADDRESS_DIRECT;
   }
   | indirectgenreg
   {
      $$ = $1;
      $$.address_mode = BRW_ADDRESS_REGISTER_INDIRECT_REGISTER;
   }
   ;

/* Source register */
srcaccimm:
   srcacc
   | immreg
   ;

immreg:
   immval imm_type
   {
      switch ($2) {
      case BRW_TYPE_UD:
         $$ = brw_imm_ud($1);
         break;
      case BRW_TYPE_D:
         $$ = brw_imm_d($1);
         break;
      case BRW_TYPE_UW:
         $$ = brw_imm_uw($1 | ($1 << 16));
         break;
      case BRW_TYPE_W:
         $$ = brw_imm_w($1);
         break;
      case BRW_TYPE_F:
         $$ = brw_imm_reg(BRW_TYPE_F);
         /* Set u64 instead of ud since DIM uses a 64-bit F-typed imm */
         $$.u64 = $1;
         break;
      case BRW_TYPE_V:
         $$ = brw_imm_v($1);
         break;
      case BRW_TYPE_UV:
         $$ = brw_imm_uv($1);
         break;
      case BRW_TYPE_VF:
         $$ = brw_imm_vf($1);
         break;
      case BRW_TYPE_Q:
         $$ = brw_imm_q($1);
         break;
      case BRW_TYPE_UQ:
         $$ = brw_imm_uq($1);
         break;
      case BRW_TYPE_DF:
         $$ = brw_imm_reg(BRW_TYPE_DF);
         $$.d64 = $1;
         break;
      case BRW_TYPE_HF:
         $$ = brw_imm_reg(BRW_TYPE_HF);
         $$.ud = $1 | ($1 << 16);
         break;
      default:
         error(&@2, "Unknown immediate type %s\n",
               brw_reg_type_to_letters($2));
      }
   }
   ;

reg32:
   directgenreg region reg_type
   {
      $$ = set_direct_src_operand(&$1, $3);
      $$ = stride($$, $2.vstride, $2.width, $2.hstride);
   }
   ;

payload:
   directsrcoperand
   ;

src:
   directsrcoperand
   | indirectsrcoperand
   ;

srcacc:
   directsrcaccoperand
   | indirectsrcoperand
   ;

srcimm:
   directsrcoperand
   | indirectsrcoperand
   | immreg
   ;

directsrcaccoperand:
   directsrcoperand
   | negate abs accreg region reg_type
   {
      $$ = set_direct_src_operand(&$3, $5);
      $$.negate = $1;
      $$.abs = $2;
      $$.vstride = $4.vstride;
      $$.width = $4.width;
      $$.hstride = $4.hstride;
   }
   ;

srcarcoperandex:
   srcarcoperandex_typed region reg_type
   {
      $$ = brw_make_reg($1.file,
                        $1.nr,
                        $1.subnr,
                        0,
                        0,
                        $3,
                        $2.vstride,
                        $2.width,
                        $2.hstride,
                        BRW_SWIZZLE_NOOP,
                        WRITEMASK_XYZW);
   }
   | nullreg region reg_type
   {
      $$ = set_direct_src_operand(&$1, $3);
      $$.vstride = $2.vstride;
      $$.width = $2.width;
      $$.hstride = $2.hstride;
   }
   | threadcontrolreg
   {
      $$ = set_direct_src_operand(&$1, BRW_TYPE_UW);
   }
   ;

srcarcoperandex_typed:
   channelenablereg
   | controlreg
   | flagreg
   | ipreg
   | maskreg
   | statereg
   | scalarreg
   ;

indirectsrcoperand:
   negate abs indirectgenreg indirectregion swizzle reg_type
   {
      $$ = brw_make_reg($3.file,
                        0,
                        $3.subnr,
                        $1,  // negate
                        $2,  // abs
                        $6,
                        $4.vstride,
                        $4.width,
                        $4.hstride,
                        $5,
                        WRITEMASK_X);

      $$.address_mode = BRW_ADDRESS_REGISTER_INDIRECT_REGISTER;
      // brw_reg set indirect_offset to 0 so set it to valid value
      $$.indirect_offset = $3.indirect_offset;
   }
   ;

directgenreg_list:
   directgenreg
   | notifyreg
   | addrreg
   | performancereg
   ;

directsrcoperand:
   negate abs directgenreg_list region swizzle reg_type
   {
      $$ = brw_make_reg($3.file,
                        $3.nr,
                        $3.subnr,
                        $1,
                        $2,
                        $6,
                        $4.vstride,
                        $4.width,
                        $4.hstride,
                        $5,
                        WRITEMASK_X);
   }
   | srcarcoperandex
   ;

/* Address register */
addrparam:
   addrreg exp
   {
      memset(&$$, '\0', sizeof($$));
      $$.subnr = $1.subnr;
      $$.indirect_offset = $2;
   }
   | addrreg
   ;

/* Register files and register numbers */
exp:
   INTEGER    { $$ = $1; }
   | LONG     { $$ = $1; }
   ;

subregnum:
   DOT exp                          { $$ = $2; }
   | /* empty */ %prec SUBREGNUM    { $$ = 0; }
   ;

directgenreg:
   GENREG subregnum
   {
      memset(&$$, '\0', sizeof($$));
      $$.file = FIXED_GRF;
      $$.nr = $1 * reg_unit(p->devinfo);
      $$.subnr = $2;
   }
   ;

indirectgenreg:
   GENREGFILE LSQUARE addrparam RSQUARE
   {
      memset(&$$, '\0', sizeof($$));
      $$.file = FIXED_GRF;
      $$.subnr = $3.subnr;
      $$.indirect_offset = $3.indirect_offset;
   }
   ;

addrreg:
   ADDRREG subregnum
   {
      int subnr = 16;

      if ($2 > subnr)
         error(&@2, "Address sub register number %d"
               "out of range\n", $2);

      $$.file = ARF;
      $$.nr = BRW_ARF_ADDRESS;
      $$.subnr = $2;
   }
   ;

accreg:
   ACCREG subregnum
   {
      int nr_reg = 10;

      if ($1 > nr_reg)
         error(&@1, "Accumulator register number %d"
               " out of range\n", $1);

      memset(&$$, '\0', sizeof($$));
      $$.file = ARF;
      $$.nr = BRW_ARF_ACCUMULATOR;
      $$.subnr = $2;
   }
   ;

flagreg:
   FLAGREG subregnum
   {
      // 2 flag reg
      int nr_reg = 2;
      int subnr = nr_reg;

      if ($1 > nr_reg)
         error(&@1, "Flag register number %d"
               " out of range \n", $1);
      if ($2 > subnr)
         error(&@2, "Flag subregister number %d"
               " out of range\n", $2);

      $$.file = ARF;
      $$.nr = BRW_ARF_FLAG | $1;
      $$.subnr = $2;
   }
   ;

maskreg:
   MASKREG subregnum
   {
      if ($1 > 0)
         error(&@1, "Mask register number %d"
               " out of range\n", $1);

      $$.file = ARF;
      $$.nr = BRW_ARF_MASK;
      $$.subnr = $2;
   }
   ;

notifyreg:
   NOTIFYREG subregnum
   {
      int subnr = (p->devinfo->ver >= 11) ? 2 : 3;
      if ($2 > subnr)
         error(&@2, "Notification sub register number %d"
               " out of range\n", $2);

      $$.file = ARF;
      $$.nr = BRW_ARF_NOTIFICATION_COUNT;
      $$.subnr = $2;
   }
   ;

scalarreg:
   SCALARREG subregnum
   {
      if ($2 > 31)
         error(&@2, "Scalar sub register number %d"
                    " out of range\n", $2);

      $$.file = ARF;
      $$.nr = BRW_ARF_SCALAR;
      $$.subnr = $2;
   }

statereg:
   STATEREG subregnum
   {
      if ($1 > 2)
         error(&@1, "State register number %d"
               " out of range\n", $1);

      if ($2 > 4)
         error(&@2, "State sub register number %d"
               " out of range\n", $2);

      $$.file = ARF;
      $$.nr = BRW_ARF_STATE;
      $$.subnr = $2;
   }
   ;

controlreg:
   CONTROLREG subregnum
   {
      if ($2 > 3)
         error(&@2, "control sub register number %d"
               " out of range\n", $2);

      $$.file = ARF;
      $$.nr = BRW_ARF_CONTROL;
      $$.subnr = $2;
   }
   ;

ipreg:
   IPREG      { $$ = brw_ip_reg(); }
   ;

nullreg:
   NULL_TOKEN    { $$ = brw_null_reg(); }
   ;

threadcontrolreg:
   THREADREG subregnum
   {
      if ($2 > 7)
         error(&@2, "Thread control sub register number %d"
               " out of range\n", $2);

      $$.file = ARF;
      $$.nr = BRW_ARF_TDR;
      $$.subnr = $2;
   }
   ;

performancereg:
   PERFORMANCEREG subregnum
   {
      int subnr;
      if (p->devinfo->ver >= 10)
         subnr = 5;
      else
         subnr = 4;

      if ($2 > subnr)
         error(&@2, "Performance sub register number %d"
               " out of range\n", $2);

      $$.file = ARF;
      $$.nr = BRW_ARF_TIMESTAMP;
      $$.subnr = $2;
   }
   ;

channelenablereg:
   CHANNELENABLEREG subregnum
   {
      if ($1 > 0)
         error(&@1, "Channel enable register number %d"
               " out of range\n", $1);

      $$.file = ARF;
      $$.nr = BRW_ARF_MASK;
      $$.subnr = $2;
   }
   ;

/* Immediate values */
immval:
   exp2
   {
      $$ = $1;
   }
   | LSQUARE exp2 COMMA exp2 COMMA exp2 COMMA exp2 RSQUARE
   {
      $$ = ($2 << 0) | ($4 << 8) | ($6 << 16) | ($8 << 24);
   }
   ;

/* Regions */
dstregion:
   /* empty */
   {
      $$ = BRW_HORIZONTAL_STRIDE_1;
   }
   | LANGLE exp RANGLE
   {
      if ($2 != 0 && ($2 > 4 || !isPowerofTwo($2)))
         error(&@2, "Invalid Horizontal stride %d\n", $2);

      $$ = ffs($2);
   }
   ;

indirectregion:
   region
   | region_wh
   ;

region:
   /* empty */
   {
      $$ = stride($$, 0, 1, 0);
   }
   | LANGLE exp RANGLE
   {
      if ($2 != 0 && ($2 > 32 || !isPowerofTwo($2)))
         error(&@2, "Invalid VertStride %d\n", $2);

      $$ = stride($$, $2, 1, 0);
   }
   | LANGLE exp COMMA exp COMMA exp RANGLE
   {

      if ($2 != 0 && ($2 > 32 || !isPowerofTwo($2)))
         error(&@2, "Invalid VertStride %d\n", $2);

      if ($4 > 16 || !isPowerofTwo($4))
         error(&@4, "Invalid width %d\n", $4);

      if ($6 != 0 && ($6 > 4 || !isPowerofTwo($6)))
         error(&@6, "Invalid Horizontal stride in"
               "  region_wh %d\n", $6);

      $$ = stride($$, $2, $4, $6);
   }
   | LANGLE exp SEMICOLON exp COMMA exp RANGLE
   {
      if ($2 != 0 && ($2 > 32 || !isPowerofTwo($2)))
         error(&@2, "Invalid VertStride %d\n", $2);

      if ($4 > 16 || !isPowerofTwo($4))
         error(&@4, "Invalid width %d\n", $4);

      if ($6 != 0 && ($6 > 4 || !isPowerofTwo($6)))
         error(&@6, "Invalid Horizontal stride in"
               " region_wh %d\n", $6);

      $$ = stride($$, $2, $4, $6);
   }
   | LANGLE VxH COMMA exp COMMA exp RANGLE
   {
      if ($4 > 16 || !isPowerofTwo($4))
         error(&@4, "Invalid width %d\n", $4);

      if ($6 != 0 && ($6 > 4 || !isPowerofTwo($6)))
         error(&@6, "Invalid Horizontal stride in"
               " region_wh %d\n", $6);

      $$ = brw_VxH_indirect(0, 0);
   }
   ;

region_wh:
   LANGLE exp COMMA exp RANGLE
   {
      if ($2 > 16 || !isPowerofTwo($2))
         error(&@2, "Invalid width %d\n", $2);

      if ($4 != 0 && ($4 > 4 || !isPowerofTwo($4)))
         error(&@4, "Invalid Horizontal stride in"
               " region_wh %d\n", $4);

      $$ = stride($$, 0, $2, $4);
      $$.vstride = BRW_VERTICAL_STRIDE_ONE_DIMENSIONAL;
   }
   ;

reg_type:
     TYPE_F    { $$ = BRW_TYPE_F;  }
   | TYPE_UD   { $$ = BRW_TYPE_UD; }
   | TYPE_D    { $$ = BRW_TYPE_D;  }
   | TYPE_UW   { $$ = BRW_TYPE_UW; }
   | TYPE_W    { $$ = BRW_TYPE_W;  }
   | TYPE_UB   { $$ = BRW_TYPE_UB; }
   | TYPE_B    { $$ = BRW_TYPE_B;  }
   | TYPE_DF   { $$ = BRW_TYPE_DF; }
   | TYPE_UQ   { $$ = BRW_TYPE_UQ; }
   | TYPE_Q    { $$ = BRW_TYPE_Q;  }
   | TYPE_HF   { $$ = BRW_TYPE_HF; }
   | TYPE_BF   { $$ = BRW_TYPE_BF; }
   ;

imm_type:
   reg_type    { $$ = $1; }
   | TYPE_V    { $$ = BRW_TYPE_V;  }
   | TYPE_VF   { $$ = BRW_TYPE_VF; }
   | TYPE_UV   { $$ = BRW_TYPE_UV; }
   ;

writemask:
   /* empty */
   {
      $$ = WRITEMASK_XYZW;
   }
   | DOT writemask_x writemask_y writemask_z writemask_w
   {
      $$ = $2 | $3 | $4 | $5;
   }
   ;

writemask_x:
   /* empty */    { $$ = 0; }
   | X            { $$ = 1 << BRW_CHANNEL_X; }
   ;

writemask_y:
   /* empty */    { $$ = 0; }
   | Y            { $$ = 1 << BRW_CHANNEL_Y; }
   ;

writemask_z:
   /* empty */    { $$ = 0; }
   | Z            { $$ = 1 << BRW_CHANNEL_Z; }
   ;

writemask_w:
   /* empty */    { $$ = 0; }
   | W            { $$ = 1 << BRW_CHANNEL_W; }
   ;

swizzle:
   /* empty */
   {
      $$ = BRW_SWIZZLE_NOOP;
   }
   | DOT chansel
   {
      $$ = BRW_SWIZZLE4($2, $2, $2, $2);
   }
   | DOT chansel chansel chansel chansel
   {
      $$ = BRW_SWIZZLE4($2, $3, $4, $5);
   }
   ;

chansel:
   X
   | Y
   | Z
   | W
   ;

/* Instruction prediction and modifiers */
predicate:
   /* empty */
   {
      brw_push_insn_state(p);
      brw_set_default_predicate_control(p, BRW_PREDICATE_NONE);
      brw_set_default_flag_reg(p, 0, 0);
      brw_set_default_predicate_inverse(p, false);
   }
   | LPAREN predstate flagreg predctrl RPAREN
   {
      brw_push_insn_state(p);
      brw_set_default_predicate_inverse(p, $2);
      brw_set_default_flag_reg(p, $3.nr, $3.subnr);
      brw_set_default_predicate_control(p, $4);
   }
   ;

predstate:
   /* empty */     { $$ = 0; }
   | PLUS          { $$ = 0; }
   | MINUS         { $$ = 1; }
   ;

predctrl:
   /* empty */    { $$ = BRW_PREDICATE_NORMAL; }
   | DOT X        { $$ = BRW_PREDICATE_ALIGN16_REPLICATE_X; }
   | DOT Y        { $$ = BRW_PREDICATE_ALIGN16_REPLICATE_Y; }
   | DOT Z        { $$ = BRW_PREDICATE_ALIGN16_REPLICATE_Z; }
   | DOT W        { $$ = BRW_PREDICATE_ALIGN16_REPLICATE_W; }
   | ANYV
   | ALLV
   | ANY2H
   | ALL2H
   | ANY4H
   | ALL4H
   | ANY8H
   | ALL8H
   | ANY16H
   | ALL16H
   | ANY32H
   | ALL32H
   ;

/* Source Modification */
negate:
   /* empty */   { $$ = 0; }
   | MINUS       { $$ = 1; }
   ;

abs:
   /* empty */   { $$ = 0; }
   | ABS         { $$ = 1; }
   ;

/* Flag (Conditional) Modifier */
cond_mod:
   condModifiers
   {
      $$.cond_modifier = $1;
      $$.flag_reg_nr = 0;
      $$.flag_subreg_nr = 0;
   }
   | condModifiers DOT flagreg
   {
      $$.cond_modifier = $1;
      $$.flag_reg_nr = $3.nr;
      $$.flag_subreg_nr = $3.subnr;
   }
   ;

condModifiers:
   /* empty */    { $$ = BRW_CONDITIONAL_NONE; }
   | ZERO
   | EQUAL
   | NOT_ZERO
   | NOT_EQUAL
   | GREATER
   | GREATER_EQUAL
   | LESS
   | LESS_EQUAL
   | OVERFLOW
   | ROUND_INCREMENT
   | UNORDERED
   ;

/* message details for send */
msgdesc:
   MSGDESC_BEGIN msgdesc_parts MSGDESC_END { $$ = $2; }
   ;

msgdesc_parts:
   SRC1_LEN ASSIGN INTEGER msgdesc_parts
   {
      $$ = $4;
      $$.src1_len = $3;
   }
   | EX_BSO msgdesc_parts
   {
      $$ = $2;
      $$.ex_bso = 1;
   }
   | INTEGER msgdesc_parts { $$ = $2; }
   | ASSIGN msgdesc_parts { $$ = $2; }
   | /* empty */
   {
      memset(&$$, 0, sizeof($$));
   }
   ;

saturate:
   /* empty */   { $$ = BRW_INSTRUCTION_NORMAL; }
   | SATURATE    { $$ = BRW_INSTRUCTION_SATURATE; }
   ;

/* Execution size */
execsize:
   /* empty */ %prec EMPTYEXECSIZE
   {
      $$ = 0;
   }
   | LPAREN exp2 RPAREN
   {
      if ($2 > 32 || !isPowerofTwo($2))
         error(&@2, "Invalid execution size %llu\n", $2);

      $$ = cvt($2) - 1;
   }
   ;

/* Instruction options */
instoptions:
   /* empty */
   {
      memset(&$$, 0, sizeof($$));
   }
   | LCURLY instoption_list RCURLY
   {
      memset(&$$, 0, sizeof($$));
      $$ = $2;
   }
   ;

instoption_list:
   instoption_list COMMA instoption
   {
      memset(&$$, 0, sizeof($$));
      $$ = $1;
      add_instruction_option(&$$, $3);
   }
   | instoption_list instoption
   {
      memset(&$$, 0, sizeof($$));
      $$ = $1;
      add_instruction_option(&$$, $2);
   }
   | /* empty */
   {
      memset(&$$, 0, sizeof($$));
   }
   ;

depinfo:
   REG_DIST_CURRENT
   {
      memset(&$$, 0, sizeof($$));
      $$.regdist = $1;
      $$.pipe = TGL_PIPE_NONE;
   }
   | REG_DIST_FLOAT
   {
      memset(&$$, 0, sizeof($$));
      $$.regdist = $1;
      $$.pipe = TGL_PIPE_FLOAT;
   }
   | REG_DIST_INT
   {
      memset(&$$, 0, sizeof($$));
      $$.regdist = $1;
      $$.pipe = TGL_PIPE_INT;
   }
   | REG_DIST_LONG
   {
      memset(&$$, 0, sizeof($$));
      $$.regdist = $1;
      $$.pipe = TGL_PIPE_LONG;
   }
   | REG_DIST_ALL
   {
      memset(&$$, 0, sizeof($$));
      $$.regdist = $1;
      $$.pipe = TGL_PIPE_ALL;
   }
   | REG_DIST_MATH
   {
      memset(&$$, 0, sizeof($$));
      $$.regdist = $1;
      $$.pipe = TGL_PIPE_MATH;
   }
   | REG_DIST_SCALAR
   {
      memset(&$$, 0, sizeof($$));
      $$.regdist = $1;
      $$.pipe = TGL_PIPE_SCALAR;
   }
   | SBID_ALLOC
   {
      memset(&$$, 0, sizeof($$));
      $$.sbid = $1;
      $$.mode = TGL_SBID_SET;
   }
   | SBID_WAIT_SRC
   {
      memset(&$$, 0, sizeof($$));
      $$.sbid = $1;
      $$.mode = TGL_SBID_SRC;
   }
   | SBID_WAIT_DST
   {
      memset(&$$, 0, sizeof($$));
      $$.sbid = $1;
      $$.mode = TGL_SBID_DST;
   }

instoption:
   ALIGN1          { $$.type = INSTOPTION_FLAG; $$.uint_value = ALIGN1;}
   | ALIGN16       { $$.type = INSTOPTION_FLAG; $$.uint_value = ALIGN16; }
   | ACCWREN
   {
      if (p->devinfo->ver >= 20)
         error(&@1, "AccWrEnable not supported in Xe2+\n");
      $$.type = INSTOPTION_FLAG;
      $$.uint_value = ACCWREN;
   }
   | BREAKPOINT    { $$.type = INSTOPTION_FLAG; $$.uint_value = BREAKPOINT; }
   | NODDCLR       { $$.type = INSTOPTION_FLAG; $$.uint_value = NODDCLR; }
   | NODDCHK       { $$.type = INSTOPTION_FLAG; $$.uint_value = NODDCHK; }
   | MASK_DISABLE  { $$.type = INSTOPTION_FLAG; $$.uint_value = MASK_DISABLE; }
   | EOT           { $$.type = INSTOPTION_FLAG; $$.uint_value = EOT; }
   | SWITCH        { $$.type = INSTOPTION_FLAG; $$.uint_value = SWITCH; }
   | ATOMIC        { $$.type = INSTOPTION_FLAG; $$.uint_value = ATOMIC; }
   | BRANCH_CTRL   { $$.type = INSTOPTION_FLAG; $$.uint_value = BRANCH_CTRL; }
   | CMPTCTRL      { $$.type = INSTOPTION_FLAG; $$.uint_value = CMPTCTRL; }
   | WECTRL        { $$.type = INSTOPTION_FLAG; $$.uint_value = WECTRL; }
   | QTR_2Q        { $$.type = INSTOPTION_CHAN_OFFSET; $$.uint_value = 8; }
   | QTR_3Q        { $$.type = INSTOPTION_CHAN_OFFSET; $$.uint_value = 16; }
   | QTR_4Q        { $$.type = INSTOPTION_CHAN_OFFSET; $$.uint_value = 24; }
   | QTR_2H        { $$.type = INSTOPTION_CHAN_OFFSET; $$.uint_value = 16; }
   | QTR_2N
   {
      if (p->devinfo->ver >= 20)
         error(&@1, "Channel offset must be multiple of 8 in Xe2+\n");
      $$.type = INSTOPTION_CHAN_OFFSET;
      $$.uint_value = 4;
   }
   | QTR_3N        { $$.type = INSTOPTION_CHAN_OFFSET; $$.uint_value = 8; }
   | QTR_4N
   {
      if (p->devinfo->ver >= 20)
         error(&@1, "Channel offset must be multiple of 8 in Xe2+\n");
      $$.type = INSTOPTION_CHAN_OFFSET; $$.uint_value = 12;
   }
   | QTR_5N        { $$.type = INSTOPTION_CHAN_OFFSET; $$.uint_value = 16; }
   | QTR_6N
   {
      if (p->devinfo->ver >= 20)
         error(&@1, "Channel offset must be multiple of 8 in Xe2+\n");
      $$.type = INSTOPTION_CHAN_OFFSET; $$.uint_value = 20;
   }
   | QTR_7N        { $$.type = INSTOPTION_CHAN_OFFSET; $$.uint_value = 24; }
   | QTR_8N
   {
      if (p->devinfo->ver >= 20)
         error(&@1, "Channel offset must be multiple of 8 in Xe2+\n");
      $$.type = INSTOPTION_CHAN_OFFSET; $$.uint_value = 28;
   }
   | depinfo       { $$.type = INSTOPTION_DEP_INFO; $$.depinfo_value = $1; }
   ;

%%

extern int yylineno;

#ifdef YYBYACC
void
yyerror(YYLTYPE *ltype, char *msg)
#else
void
yyerror(char *msg)
#endif
{
   fprintf(stderr, "%s: %d: %s at \"%s\"\n",
           input_filename, yylineno, msg, lex_text());
   ++errors;
}
