{ Copyright (c) 1998-2002 by Carl Eric Codere and Peter Vreman Does the parsing for the x86 GNU AS styled inline assembler. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. **************************************************************************** } Unit rax86att; {$i fpcdefs.inc} Interface uses cpubase, raatt,rax86, rautils; type { tx86attreader } tx86attreader = class(tattreader) ActOpsize : topsize; function is_asmopcode(const s: string):boolean;override; procedure handleopcode;override; procedure BuildReference(oper : tx86operand); procedure BuildOperand(oper : tx86operand); procedure BuildOpCode(instr : tx86instruction); procedure handlepercent;override; protected procedure MaybeGetPICModifier(var oper: tx86operand); end; { Tx86attInstruction } Tx86attInstruction = class(Tx86Instruction) procedure AddReferenceSizes; override; procedure FixupOpcode;override; end; Implementation uses { helpers } cutils, { global } globtype,verbose, systems, { aasm } aasmbase,aasmdata,aasmcpu, { symtable } symconst,symsym,symdef, { parser } scanner, procinfo, itcpugas, paramgr, cgbase ; { Tx86attInstruction } procedure Tx86attInstruction.AddReferenceSizes; var i: integer; begin if (Opsize <> S_NO) and (MemRefInfo(opcode).ExistsSSEAVX) and (MemRefInfo(opcode).MemRefSize in MemRefMultiples) then begin for i := 1 to ops do begin if operands[i].Opr.Typ in [OPR_REFERENCE, OPR_LOCAL] then begin if (tx86operand(operands[i]).opsize = S_NO) then tx86operand(operands[i]).opsize := Opsize; end; end; end; inherited AddReferenceSizes; end; procedure Tx86attInstruction.FixupOpcode; begin case opcode of A_MOVQ: begin { May be either real 'movq' or a generic 'mov' with 'q' suffix. Convert to mov if source is a constant, or if neither operand is an mmx/xmm register } {$ifdef x86_64} if (ops=2) and ( (operands[1].opr.typ=OPR_CONSTANT) or not ( ((operands[1].opr.typ=OPR_REGISTER) and (getregtype(operands[1].opr.reg) in [R_MMXREGISTER,R_MMREGISTER])) or ((operands[2].opr.typ=OPR_REGISTER) and (getregtype(operands[2].opr.reg) in [R_MMXREGISTER,R_MMREGISTER])) ) ) then opcode:=A_MOV; {$endif x86_64} end; else ; end; end; { Tx86attReader } procedure tx86attreader.handlepercent; var len : longint; begin len:=1; actasmpattern[len]:='%'; c:=current_scanner.asmgetchar; { to be a register there must be a letter and not a number } if c in ['0'..'9'] then begin actasmtoken:=AS_MOD; end else begin while c in ['a'..'z','A'..'Z','0'..'9'] do Begin inc(len); actasmpattern[len]:=c; c:=current_scanner.asmgetchar; end; actasmpattern[0]:=chr(len); uppervar(actasmpattern); if (actasmpattern = '%ST') and (c='(') then Begin actasmpattern:=actasmpattern+c; c:=current_scanner.asmgetchar; if c in ['0'..'9'] then actasmpattern:=actasmpattern + c else Message(asmr_e_invalid_fpu_register); c:=current_scanner.asmgetchar; if c <> ')' then Message(asmr_e_invalid_fpu_register) else Begin actasmpattern:=actasmpattern + c; c:=current_scanner.asmgetchar; { let us point to next character. } end; end; if is_register(actasmpattern) then exit; Message(asmr_e_invalid_register); actasmtoken:=raatt.AS_NONE; end; end; Procedure tx86attreader.BuildReference(oper : tx86operand); procedure Consume_RParen; begin if actasmtoken <> AS_RPAREN then Begin Message(asmr_e_invalid_reference_syntax); RecoverConsume(true); end else begin Consume(AS_RPAREN); if not (actasmtoken in [AS_COMMA,AS_SEPARATOR,AS_END]) then Begin Message(asmr_e_invalid_reference_syntax); RecoverConsume(true); end; end; end; procedure Consume_Scale; var l : aint; begin { we have to process the scaling } l:=BuildConstExpression(false,true); if ((l = 2) or (l = 4) or (l = 8) or (l = 1)) then oper.opr.ref.scalefactor:=l else Begin Message(asmr_e_wrong_scale_factor); oper.opr.ref.scalefactor:=0; end; end; procedure Consume_Index; procedure Check_Scaling; begin { check for scaling ... } case actasmtoken of AS_RPAREN: Begin Consume_RParen; exit; end; AS_COMMA: Begin Consume(AS_COMMA); Consume_Scale; Consume_RParen; end; else Begin Message(asmr_e_invalid_reference_syntax); RecoverConsume(false); end; end; { end case } end; var tmp : tx86operand; expr : string; begin if actasmtoken=AS_REGISTER then Begin oper.opr.ref.index:=actasmregister; Consume(AS_REGISTER); Check_Scaling; end else if actasmtoken=AS_ID then begin expr:=actasmpattern; Consume(AS_ID); tmp:=Tx86Operand.create; if not tmp.SetupVar(expr,false) then begin { look for special symbols ... } if expr= '__HIGH' then begin consume(AS_LPAREN); if not tmp.setupvar('high'+actasmpattern,false) then Message1(sym_e_unknown_id,'high'+actasmpattern); consume(AS_ID); consume(AS_RPAREN); end else if expr = '__SELF' then tmp.SetupSelf else begin message1(sym_e_unknown_id,expr); RecoverConsume(false); tmp.free; Exit; end; end; { convert OPR_LOCAL register para into a reference base } if (tmp.opr.typ=OPR_LOCAL) and AsmRegisterPara(tmp.opr.localsym) then begin tmp.InitRefConvertLocal; if (tmp.opr.ref.index<>NR_NO) or (tmp.opr.ref.offset<>0) or (tmp.opr.ref.scalefactor<>0) or (tmp.opr.ref.segment<>NR_NO) or (tmp.opr.ref.base=NR_NO) then begin message(asmr_e_invalid_reference_syntax); RecoverConsume(false); tmp.free; Exit; end; oper.opr.ref.index:=tmp.opr.ref.base; tmp.free; Check_Scaling; end else begin message(asmr_e_invalid_reference_syntax); RecoverConsume(false); tmp.free; Exit; end; end else Begin Message(asmr_e_invalid_reference_syntax); RecoverConsume(false); end; end; var expr : string; tmp : tx86operand; begin oper.InitRef; Consume(AS_LPAREN); Case actasmtoken of AS_INTNUM, AS_MINUS, AS_PLUS: { absolute offset, such as fs:(0x046c) } Begin { offset(offset) is invalid } If oper.opr.Ref.Offset <> 0 Then Begin Message(asmr_e_invalid_reference_syntax); RecoverConsume(true); End Else Begin oper.opr.Ref.Offset:=BuildConstExpression(false,true); Consume_RParen; end; exit; End; AS_REGISTER: { (reg ... } Begin { Check if there is already a base (mostly ebp,esp) than this is not allowed, because it will give crashing code } if ((oper.opr.typ=OPR_REFERENCE) and (oper.opr.ref.base<>NR_NO)) or ((oper.opr.typ=OPR_LOCAL) and (oper.opr.localsym.localloc.loc<>LOC_REGISTER)) then message(asmr_e_cannot_index_relative_var); oper.opr.ref.base:=actasmregister; {$ifdef x86_64} { non-GOT based RIP-relative accesses are also position-independent } if (oper.opr.ref.base=NR_RIP) and (oper.opr.ref.refaddr<>addr_pic) then oper.opr.ref.refaddr:=addr_pic_no_got; {$endif x86_64} Consume(AS_REGISTER); { can either be a register, an identifier or a right parenthesis } { (reg) } if actasmtoken=AS_RPAREN then Begin Consume_RParen; exit; end; { (reg,reg .. } Consume(AS_COMMA); Consume_Index; end; {end case } AS_ID: { identifier (parameter, variable, ...), but only those that might be in a register } begin expr:=actasmpattern; Consume(AS_ID); tmp:=Tx86Operand.create; if not tmp.SetupVar(expr,false) then begin { look for special symbols ... } if expr= '__HIGH' then begin consume(AS_LPAREN); if not tmp.setupvar('high'+actasmpattern,false) then Message1(sym_e_unknown_id,'high'+actasmpattern); consume(AS_ID); consume(AS_RPAREN); end else if expr = '__SELF' then tmp.SetupSelf else begin message1(sym_e_unknown_id,expr); RecoverConsume(false); tmp.free; Exit; end; end; { convert OPR_LOCAL register para into a reference base } if (tmp.opr.typ=OPR_LOCAL) and AsmRegisterPara(tmp.opr.localsym) then begin tmp.InitRefConvertLocal; if (tmp.opr.ref.index<>NR_NO) or (tmp.opr.ref.offset<>0) or (tmp.opr.ref.scalefactor<>0) or (tmp.opr.ref.segment<>NR_NO) or (tmp.opr.ref.base=NR_NO) then begin message(asmr_e_invalid_reference_syntax); RecoverConsume(false); tmp.free; Exit; end; oper.opr.ref.base:=tmp.opr.ref.base; tmp.free; end else begin message(asmr_e_invalid_reference_syntax); RecoverConsume(false); tmp.free; Exit; end; { can either be a register, an identifier or a right parenthesis } { (reg) } if actasmtoken=AS_RPAREN then begin Consume_RParen; exit; end; Consume(AS_COMMA); Consume_Index; end; AS_COMMA: { (, ... can either be scaling, or index } Begin Consume(AS_COMMA); { Index } if (actasmtoken=AS_REGISTER) then Begin oper.opr.ref.index:=actasmregister; Consume(AS_REGISTER); { check for scaling ... } case actasmtoken of AS_RPAREN: Begin Consume_RParen; exit; end; AS_COMMA: Begin Consume(AS_COMMA); Consume_Scale; Consume_RParen; end; else Begin Message(asmr_e_invalid_reference_syntax); RecoverConsume(false); end; end; {end case } end { Scaling } else Begin Consume_Scale; Consume_RParen; exit; end; end; else Begin Message(asmr_e_invalid_reference_syntax); RecoverConsume(false); end; end; end; Procedure tx86attreader.MaybeGetPICModifier(var oper: tx86operand); var relsym: string; asmsymtyp: tasmsymtype; l: tcgint; sym: tasmsymbol; begin case actasmtoken of AS_AT: begin { darwin/i386 needs a relsym instead, and we can't } { generate this automatically } if (target_info.system in [system_i386_darwin,system_i386_iphonesim]) then Message(asmr_e_invalid_reference_syntax); consume(AS_AT); if actasmtoken=AS_ID then begin {$ifdef x86_64} if (actasmpattern='GOTPCREL') or (actasmpattern='PLT') then {$endif x86_64} {$ifdef i386} if actasmpattern='GOT' then {$endif i386} {$ifdef i8086} if actasmpattern='GOT' then {$endif i8086} begin case oper.opr.typ of OPR_SYMBOL: begin sym:=oper.opr.symbol; if oper.opr.symofs<>0 then Message(asmr_e_invalid_reference_syntax); oper.opr.typ:=OPR_REFERENCE; fillchar(oper.opr.ref,sizeof(oper.opr.ref),0); oper.opr.ref.symbol:=sym; end; OPR_REFERENCE: begin { ok } end; else Message(asmr_e_invalid_reference_syntax) end; oper.opr.ref.refaddr:=addr_pic; consume(AS_ID); end else Message(asmr_e_invalid_reference_syntax); end else Message(asmr_e_invalid_reference_syntax); end; AS_MINUS: begin { relsym? } Consume(AS_MINUS); BuildConstSymbolExpression(true,true,false,l,relsym,asmsymtyp); if (relsym<>'') then if not assigned(oper.opr.ref.relsymbol) then oper.opr.ref.relsymbol:=current_asmdata.RefAsmSymbol(relsym,asmsymtyp) else Message(asmr_e_invalid_reference_syntax) else dec(oper.opr.ref.offset,l); end; else ; end; end; Procedure tx86attreader.BuildOperand(oper : tx86operand); var tempstr, expr : string; typesize,l,k : tcgint; procedure AddLabelOperand(hl:tasmlabel); begin if not(actasmtoken in [AS_PLUS,AS_MINUS,AS_LPAREN]) and is_calljmp(actopcode) then begin oper.opr.typ:=OPR_SYMBOL; oper.opr.symbol:=hl; end else begin oper.InitRef; oper.opr.ref.symbol:=hl; end; end; procedure MaybeRecordOffset; var mangledname: string; hasdot : boolean; l, toffset, tsize : tcgint; begin if not(actasmtoken in [AS_DOT,AS_PLUS,AS_MINUS]) then exit; l:=0; mangledname:=''; hasdot:=(actasmtoken=AS_DOT); if hasdot then begin if expr<>'' then begin BuildRecordOffsetSize(expr,toffset,tsize,mangledname,false); if (oper.opr.typ<>OPR_CONSTANT) and (mangledname<>'') then Message(asmr_e_wrong_sym_type); inc(l,toffset); oper.SetSize(tsize,true); case oper.opr.typ of OPR_REFERENCE: oper.opr.varsize := tsize; OPR_LOCAL: oper.opr.localvarsize := tsize; else ; end; end; end; if actasmtoken in [AS_PLUS,AS_MINUS] then inc(l,BuildConstExpression(true,false)); case oper.opr.typ of OPR_LOCAL : begin { don't allow direct access to fields of parameters, because that will generate buggy code. Allow it only for explicit typecasting } if hasdot and (not oper.hastype) then checklocalsubscript(oper.opr.localsym); inc(oper.opr.localsymofs,l); inc(oper.opr.localconstoffset,l); end; OPR_CONSTANT : if (mangledname<>'') then begin if (oper.opr.val<>0) then Message(asmr_e_wrong_sym_type); oper.opr.typ:=OPR_SYMBOL; oper.opr.symbol:=current_asmdata.RefAsmSymbol(mangledname,AT_FUNCTION); end else inc(oper.opr.val,l); OPR_REFERENCE : begin inc(oper.opr.ref.offset,l); inc(oper.opr.constoffset,l); end; OPR_SYMBOL: Message(asmr_e_invalid_symbol_ref); else internalerror(200309221); end; end; function MaybeBuildReference:boolean; { Try to create a reference, if not a reference is found then false is returned } var mangledname: string; begin MaybeBuildReference:=true; case actasmtoken of AS_INTNUM: Begin { allow %segmentregister:number } if oper.opr.ref.segment<>NR_NO then begin // already done before calling oper.InitRef; if oper.opr.Ref.Offset <> 0 Then Message(asmr_e_invalid_reference_syntax) else begin oper.opr.Ref.Offset:=BuildConstExpression(true,false); if actasmtoken=AS_LPAREN then BuildReference(oper) else if (oper.opr.ref.segment <> NR_FS) and (oper.opr.ref.segment <> NR_GS) then Message(asmr_w_general_segment_with_constant); end; end else begin oper.opr.ref.offset:=BuildConstExpression(True,False); BuildReference(oper); end; end; AS_MINUS, AS_PLUS: Begin oper.opr.ref.offset:=BuildConstExpression(True,False); if actasmtoken<>AS_LPAREN then Message(asmr_e_invalid_reference_syntax) else BuildReference(oper); end; AS_LPAREN: BuildReference(oper); AS_ID: { only a variable is allowed ... } Begin tempstr:=actasmpattern; Consume(AS_ID); { typecasting? } if (actasmtoken=AS_LPAREN) and SearchType(tempstr,typesize) then begin oper.hastype:=true; Consume(AS_LPAREN); BuildOperand(oper); Consume(AS_RPAREN); if oper.opr.typ in [OPR_REFERENCE,OPR_LOCAL] then oper.SetSize(typesize,true); end else if not oper.SetupVar(tempstr,false) then Message1(sym_e_unknown_id,tempstr); { record.field ? } if actasmtoken=AS_DOT then begin BuildRecordOffsetSize(tempstr,l,k,mangledname,false); if (mangledname<>'') then Message(asmr_e_invalid_reference_syntax); inc(oper.opr.ref.offset,l); case oper.opr.typ of OPR_REFERENCE: oper.opr.varsize := k; OPR_LOCAL: oper.opr.localvarsize := k; else ; end; end; MaybeGetPICModifier(oper); case actasmtoken of AS_END, AS_SEPARATOR, AS_COMMA: ; AS_LPAREN: BuildReference(oper); else Begin Message(asmr_e_invalid_reference_syntax); Consume(actasmtoken); end; end; {end case } end; else MaybeBuildReference:=false; end; { end case } end; var tempreg : tregister; hl : tasmlabel; Begin expr:=''; case actasmtoken of AS_LPAREN: { Memory reference or constant expression } Begin oper.InitRef; BuildReference(oper); end; AS_DOLLAR: { Constant expression } Begin Consume(AS_DOLLAR); BuildConstantOperand(oper); end; AS_INTNUM, AS_MINUS, AS_PLUS: Begin { Constant memory offset } { This must absolutely be followed by ( } oper.InitRef; oper.opr.ref.offset:=asizeint(BuildConstExpression(True,False)); if actasmtoken<>AS_LPAREN then Message(asmr_e_invalid_reference_syntax) else BuildReference(oper); end; AS_STAR: { Call from memory address } Begin Consume(AS_STAR); if actasmtoken=AS_REGISTER then begin oper.opr.typ:=OPR_REGISTER; oper.opr.reg:=actasmregister; oper.SetSize(tcgsize2size[reg_cgsize(actasmregister)],true); Consume(AS_REGISTER); end else begin oper.InitRef; if not MaybeBuildReference then Message(asmr_e_syn_operand); end; { this is only allowed for call's and jmp's } if not is_calljmp(actopcode) then Message(asmr_e_syn_operand); end; AS_ID: { A constant expression, or a Variable ref. } Begin { Local Label ? } if is_locallabel(actasmpattern) then begin CreateLocalLabel(actasmpattern,hl,false); Consume(AS_ID); AddLabelOperand(hl); MaybeGetPICModifier(oper); end else { Check for label } if SearchLabel(actasmpattern,hl,false) then begin Consume(AS_ID); AddLabelOperand(hl); MaybeGetPICModifier(oper); end else { probably a variable or normal expression } { or a procedure (such as in CALL ID) } Begin { is it a constant ? } if SearchIConstant(actasmpattern,l) then Begin if not (oper.opr.typ in [OPR_NONE,OPR_CONSTANT]) then Message(asmr_e_invalid_operand_type); BuildConstantOperand(oper); end else begin expr:=actasmpattern; Consume(AS_ID); { typecasting? } if (actasmtoken=AS_LPAREN) and SearchType(expr,typesize) then begin oper.hastype:=true; Consume(AS_LPAREN); BuildOperand(oper); Consume(AS_RPAREN); if oper.opr.typ in [OPR_REFERENCE,OPR_LOCAL] then oper.SetSize(typesize,true); end else begin if oper.SetupVar(expr,false) then MaybeGetPICModifier(oper) else Begin { look for special symbols ... } if expr= '__HIGH' then begin consume(AS_LPAREN); if not oper.setupvar('high'+actasmpattern,false) then Message1(sym_e_unknown_id,'high'+actasmpattern); consume(AS_ID); consume(AS_RPAREN); end else if expr = '__RESULT' then oper.SetUpResult else if expr = '__SELF' then oper.SetupSelf else if expr = '__OLDEBP' then oper.SetupOldEBP else Message1(sym_e_unknown_id,expr); end; end; end; if oper.opr.typ<>OPR_NONE Then begin if (actasmtoken=AS_DOT) then MaybeRecordOffset; { add a constant expression? } if (actasmtoken=AS_PLUS) then begin l:=BuildConstExpression(true,false); if errorcount=0 then case oper.opr.typ of OPR_CONSTANT : inc(oper.opr.val,l); OPR_LOCAL : begin inc(oper.opr.localsymofs,l); inc(oper.opr.localconstoffset, l); end; OPR_REFERENCE : begin inc(oper.opr.ref.offset,l); inc(oper.opr.constoffset, l); end; else internalerror(2003092011); end; end; end; end; { Do we have a indexing reference, then parse it also } if actasmtoken=AS_LPAREN then BuildReference(oper); end; AS_REGISTER: { Register, a variable reference or a constant reference } Begin { save the type of register used. } tempreg:=actasmregister; Consume(AS_REGISTER); if actasmtoken = AS_COLON then Begin Consume(AS_COLON); oper.InitRef; if not is_segment_reg(tempreg) then Message(asmr_e_invalid_seg_override); {$ifdef x86_64} if (tempreg=NR_CS) or (tempreg=NR_DS) or (tempreg=NR_SS) or (tempreg=NR_ES) then Message1(asmr_w_segment_override_ignored_in_64bit_mode,gas_regname(tempreg)); {$endif x86_64} oper.opr.ref.segment:=tempreg; { This must absolutely be followed by a reference } if not MaybeBuildReference then Begin Message(asmr_e_invalid_seg_override); Consume(actasmtoken); end; end { Simple register } else if (actasmtoken in [AS_END,AS_SEPARATOR,AS_COMMA]) then Begin if not (oper.opr.typ in [OPR_NONE,OPR_REGISTER]) then Message(asmr_e_invalid_operand_type); oper.opr.typ:=OPR_REGISTER; oper.opr.reg:=tempreg; oper.SetSize(tcgsize2size[reg_cgsize(oper.opr.reg)],true); end else Message(asmr_e_syn_operand); end; AS_END, AS_SEPARATOR, AS_COMMA: ; else Begin Message(asmr_e_syn_operand); Consume(actasmtoken); end; end; { end case } end; procedure tx86attreader.BuildOpCode(instr : tx86instruction); var operandnum : longint; PrefixOp,OverrideOp: tasmop; di_param, si_param: ShortInt; Begin PrefixOp:=A_None; OverrideOp:=A_None; { prefix seg opcode / prefix opcode } repeat if is_prefix(actopcode) then begin PrefixOp:=ActOpcode; with instr do begin opcode:=ActOpcode; condition:=ActCondition; opsize:=ActOpsize; ConcatInstruction(curlist); end; Consume(AS_OPCODE); end else if is_override(actopcode) then begin OverrideOp:=ActOpcode; with instr do begin opcode:=ActOpcode; condition:=ActCondition; opsize:=ActOpsize; ConcatInstruction(curlist); end; Consume(AS_OPCODE); end else break; { allow for newline as in gas styled syntax } while actasmtoken=AS_SEPARATOR do Consume(AS_SEPARATOR); until (actasmtoken<>AS_OPCODE); { opcode } if (actasmtoken<>AS_OPCODE) then Begin Message(asmr_e_invalid_or_missing_opcode); RecoverConsume(true); exit; end; { Fill the instr object with the current state } with instr do begin Opcode:=ActOpcode; condition:=ActCondition; opsize:=ActOpsize; end; { Valid combination of prefix/override and instruction ? } if (prefixop<>A_NONE) and (NOT CheckPrefix(PrefixOp,actopcode)) then Message1(asmr_e_invalid_prefix_and_opcode,actasmpattern); if (overrideop<>A_NONE) and (NOT CheckOverride(OverrideOp,ActOpcode)) then Message1(asmr_e_invalid_override_and_opcode,actasmpattern); { We are reading operands, so opcode will be an AS_ID } operandnum:=1; Consume(AS_OPCODE); { Zero operand opcode ? } if actasmtoken in [AS_SEPARATOR,AS_END] then begin operandnum:=0; exit; end; { Read the operands } repeat case actasmtoken of AS_COMMA: { Operand delimiter } Begin if operandnum > Max_Operands then Message(asmr_e_too_many_operands) else Inc(operandnum); Consume(AS_COMMA); end; AS_SEPARATOR, AS_END : { End of asm operands for this opcode } begin break; end; else BuildOperand(instr.Operands[operandnum] as tx86operand); end; { end case } until false; instr.Ops:=operandnum; { handle string instructions with parameters } with instr do if is_x86_parameterless_string_op(opcode) and (Ops>=1) and (Ops<=2) then begin if opcode=A_MOVSD then begin { distinguish between MOVS and the SSE MOVSD instruction: MOVS must have memory 2 reference operands (there's no need to distinguish from SSE CMPSD, because the SSE version has 3 arguments and we've already checked that above) } if (Ops=2) and (operands[1].opr.typ=OPR_REFERENCE) and (operands[2].opr.typ=OPR_REFERENCE) then begin opcode:=A_MOVS; opsize:=S_L; end; end else begin opsize:=get_x86_string_op_size(opcode); opcode:=x86_param2paramless_string_op(opcode); end; end; { Check for invalid ES: overrides } if is_x86_parameterized_string_op(instr.opcode) then begin si_param:=get_x86_string_op_si_param(instr.opcode); if si_param<>-1 then begin si_param:=x86_parameterized_string_op_param_count(instr.opcode)-si_param; if si_param<=operandnum then with instr.operands[si_param] do if (opr.typ=OPR_REFERENCE) then begin if not((((opr.ref.index<>NR_NO) and (opr.ref.base=NR_NO) and (getregtype(opr.ref.index)=R_INTREGISTER) and (getsupreg(opr.ref.index)=RS_ESI)) or ((opr.ref.index=NR_NO) and (opr.ref.base<>NR_NO) and (getregtype(opr.ref.base)=R_INTREGISTER) and (getsupreg(opr.ref.base)=RS_ESI))) and (opr.ref.offset=0) and (opr.ref.scalefactor<=1) and (opr.ref.refaddr=addr_no) and (opr.ref.symbol=nil) and (opr.ref.relsymbol=nil)) then {$if defined(i8086)} Message1(asmr_w_invalid_reference,'(%si)'); {$elseif defined(i386)} Message1(asmr_w_invalid_reference,'(%esi)'); {$elseif defined(x86_64)} Message1(asmr_w_invalid_reference,'(%rsi)'); {$endif} end; end; di_param:=get_x86_string_op_di_param(instr.opcode); if di_param<>-1 then begin di_param:=x86_parameterized_string_op_param_count(instr.opcode)-di_param; if di_param<=operandnum then with instr.operands[di_param] do if (opr.typ=OPR_REFERENCE) then begin if (opr.ref.segment<>NR_NO) and (opr.ref.segment<>NR_ES) then Message(asmr_e_cannot_override_es_segment); if not((((opr.ref.index<>NR_NO) and (opr.ref.base=NR_NO) and (getregtype(opr.ref.index)=R_INTREGISTER) and (getsupreg(opr.ref.index)=RS_EDI)) or ((opr.ref.index=NR_NO) and (opr.ref.base<>NR_NO) and (getregtype(opr.ref.base)=R_INTREGISTER) and (getsupreg(opr.ref.base)=RS_EDI))) and (opr.ref.offset=0) and (opr.ref.scalefactor<=1) and (opr.ref.refaddr=addr_no) and (opr.ref.symbol=nil) and (opr.ref.relsymbol=nil)) then {$if defined(i8086)} Message1(asmr_w_invalid_reference,'(%di)'); {$elseif defined(i386)} Message1(asmr_w_invalid_reference,'(%edi)'); {$elseif defined(x86_64)} Message1(asmr_w_invalid_reference,'(%rdi)'); {$endif} end; end; { if two memory parameters, check whether their address sizes are equal } if (si_param<>-1) and (di_param<>-1) and (si_param<=operandnum) and (di_param<=operandnum) and (instr.operands[si_param].opr.typ=OPR_REFERENCE) and (instr.operands[di_param].opr.typ=OPR_REFERENCE) then begin if get_ref_address_size(instr.operands[si_param].opr.ref)<> get_ref_address_size(instr.operands[di_param].opr.ref) then Message(asmr_e_address_sizes_do_not_match); end; end; end; function tx86attreader.is_asmopcode(const s: string):boolean; var cond : string[4]; cnd : tasmcond; len, j, sufidx, suflen : longint; Begin is_asmopcode:=FALSE; actopcode:=A_None; actcondition:=C_None; actopsize:=S_NO; { search for all possible suffixes } for sufidx:=low(att_sizesuffixstr) to high(att_sizesuffixstr) do begin suflen:=length(att_sizesuffixstr[sufidx]); len:=length(s)-suflen; if copy(s,len+1,suflen)=att_sizesuffixstr[sufidx] then begin { Search opcodes } if len>0 then begin actopcode:=tasmop(PtrUInt(iasmops.Find(copy(s,1,len)))); { movsd needs special handling because it has two namings in at&t syntax (movsl for string handling and movsd for the sse instruction) while only one in intel syntax (movsd, both string and sse) this cannot be expressed by the instruction table format so we have to hack around this here } if (actopcode = A_NONE) and (upper(s) = 'MOVSD') then actopcode := A_MOVSD; { cmpsd also needs special handling for pretty much the same reasons as movsd } if (actopcode = A_NONE) and (upper(s) = 'CMPSD') then actopcode := A_CMPSD; { disambiguation between A_MOVS (movsb/movsw/movsl/movsq) and A_MOVSX (movsbw/movsbl/movswl/movsbq/movswq) } if (actopcode = A_MOVS) and (suflen=2) then actopcode := A_MOVSX; { two-letter suffix is allowed by just a few instructions (movsx,movzx), and it is always required whenever allowed } if (gas_needsuffix[actopcode]=attsufINTdual) xor (suflen=2) then continue; { We need to refuse the opcodes that require a condition } if (actopcode=A_Jcc) or (actopcode=A_SETcc) or (actopcode=A_CMOVcc) then actopcode:=A_NONE; if actopcode<>A_NONE then begin if gas_needsuffix[actopcode]=attsufFPU then actopsize:=att_sizefpusuffix[sufidx] else if gas_needsuffix[actopcode]=attsufFPUint then actopsize:=att_sizefpuintsuffix[sufidx] else if gas_needsuffix[actopcode]in[attsufMM,attsufMMS] then actopsize:=att_sizemmsuffix[sufidx] else if gas_needsuffix[actopcode]=attsufMMX then actopsize:=att_sizemmXsuffix[sufidx] else actopsize:=att_sizesuffix[sufidx]; { only accept suffix from the same category that the opcode belongs to } if (actopsize<>S_NO) or (suflen=0) then begin actasmtoken:=AS_OPCODE; is_asmopcode:=TRUE; exit; end; end; end; { not found, check condition opcodes } j:=0; while (j'' then begin for cnd:=low(TasmCond) to high(TasmCond) do if Cond=Upper(cond2str[cnd]) then begin actopcode:=CondASmOp[j]; { conditional instructions (cmovcc, setcc) use only INT suffixes; other stuff like fcmovcc is represented as group of individual instructions } if gas_needsuffix[actopcode]=attsufINT then actopsize:=att_sizesuffix[sufidx]; { only accept suffix from the same category that the opcode belongs to } if (actopsize<>S_NO) or (suflen=0) then begin actcondition:=cnd; actasmtoken:=AS_OPCODE; is_asmopcode:=TRUE; exit; end; end; end; end; inc(j); end; end; end; end; procedure tx86attreader.handleopcode; var instr : Tx86Instruction; begin instr:=Tx86attInstruction.Create(Tx86Operand); BuildOpcode(instr); instr.AddReferenceSizes; instr.SetInstructionOpsize; instr.CheckOperandSizes; instr.FixupOpcode; instr.ConcatInstruction(curlist); instr.Free; end; end.