mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-06-28 22:28:17 +02:00

* changed most of the variables in the assembler readers used to store constants from aint to tcgint as aint has only the size of the accumular while some CPUs (AVR) allow larger constants in instructions + allow access to absolute symbols with address type in inline assembler * allow absolute addresses in avr inline assembler + tests git-svn-id: trunk@37411 -
552 lines
18 KiB
ObjectPascal
552 lines
18 KiB
ObjectPascal
{
|
|
Copyright (c) 1998-2002 by Mazen NEIFER
|
|
|
|
Does the parsing for the i386 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 racpugas;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
Interface
|
|
|
|
uses
|
|
cgbase,
|
|
rautils,
|
|
raatt;
|
|
|
|
type
|
|
tMipsReader = class(tattreader)
|
|
actrel: trefaddr;
|
|
function is_asmopcode(const s: string):boolean;override;
|
|
procedure BuildOperand(oper : TOperand);
|
|
procedure BuildOpCode(instr : TInstruction);
|
|
procedure handlepercent;override;
|
|
procedure handledollar;override;
|
|
procedure handleopcode;override;
|
|
end;
|
|
|
|
|
|
Implementation
|
|
|
|
uses
|
|
{ helpers }
|
|
cutils,
|
|
{ global }
|
|
globtype,verbose,
|
|
systems,
|
|
{ aasm }
|
|
cpubase,aasmbase,aasmtai,aasmdata,aasmcpu,
|
|
{ symtable }
|
|
symconst,symsym,symdef,
|
|
{ parser }
|
|
scanner,
|
|
procinfo,
|
|
rabase,
|
|
rgbase,
|
|
itcpugas,
|
|
cgobj,paramgr
|
|
;
|
|
|
|
|
|
procedure TMipsReader.handledollar;
|
|
var
|
|
len: longint;
|
|
begin
|
|
len:=1;
|
|
actasmpattern[len]:='$';
|
|
c:=current_scanner.asmgetchar;
|
|
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);
|
|
actasmpattern:=lower(actasmpattern);
|
|
actasmregister:=gas_regnum_search(actasmpattern);
|
|
if actasmregister=NR_NO then
|
|
actasmregister:=std_regnum_search(copy(actasmpattern,2,maxint));
|
|
actasmtoken:=AS_REGISTER;
|
|
end;
|
|
|
|
|
|
procedure TMipsReader.handlepercent;
|
|
var
|
|
len : longint;
|
|
begin
|
|
len:=1;
|
|
actasmpattern[len]:='%';
|
|
c:=current_scanner.asmgetchar;
|
|
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);
|
|
actrel:=addr_no;
|
|
if (actasmpattern='%HI') then
|
|
actrel:=addr_high
|
|
else if (actasmpattern='%LO')then
|
|
actrel:=addr_low
|
|
else
|
|
Message(asmr_e_invalid_reference_syntax);
|
|
if actrel<>addr_no then
|
|
actasmtoken:=AS_RELTYPE;
|
|
end;
|
|
|
|
|
|
Procedure TMipsReader.BuildOperand(oper : TOperand);
|
|
var
|
|
expr : string;
|
|
typesize,l : 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);
|
|
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) and
|
|
(oper.opr.localsym.typ=paravarsym) and
|
|
(not(po_assembler in current_procinfo.procdef.procoptions) or
|
|
(tparavarsym(oper.opr.localsym).paraloc[calleeside].location^.loc<>LOC_REGISTER) or
|
|
(not is_implicit_pointer_object_type(oper.opr.localsym.vardef) and
|
|
not paramanager.push_addr_param(oper.opr.localsym.varspez,oper.opr.localsym.vardef,current_procinfo.procdef.proccalloption))) then
|
|
Message(asmr_e_cannot_access_field_directly_for_parameters);
|
|
inc(oper.opr.localsymofs,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 :
|
|
inc(oper.opr.ref.offset,l);
|
|
OPR_SYMBOL:
|
|
Message(asmr_e_invalid_symbol_ref);
|
|
else
|
|
internalerror(200309221);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
tempreg : tregister;
|
|
tempstr : string;
|
|
tempsymtyp : TAsmSymType;
|
|
hl : tasmlabel;
|
|
gotplus,
|
|
negative : boolean;
|
|
Begin
|
|
expr:='';
|
|
gotplus:=true;
|
|
negative:=false;
|
|
repeat
|
|
case actasmtoken of
|
|
AS_MINUS :
|
|
begin
|
|
consume(AS_MINUS);
|
|
negative:=true;
|
|
gotplus:=true;
|
|
end;
|
|
|
|
AS_PLUS :
|
|
begin
|
|
consume(AS_PLUS);
|
|
negative:=false;
|
|
gotplus:=true;
|
|
end;
|
|
|
|
AS_INTNUM:
|
|
Begin
|
|
if not gotplus then
|
|
Message(asmr_e_invalid_reference_syntax);
|
|
l:=BuildConstExpression(True,False);
|
|
if negative then
|
|
l:=-l;
|
|
case oper.opr.typ of
|
|
OPR_NONE:
|
|
begin
|
|
oper.opr.typ:=OPR_CONSTANT;
|
|
oper.opr.val:=l;
|
|
end;
|
|
OPR_CONSTANT :
|
|
inc(oper.opr.val,l);
|
|
OPR_REFERENCE:
|
|
inc(oper.opr.ref.offset,l);
|
|
OPR_LOCAL:
|
|
inc(oper.opr.localsymofs,l);
|
|
else
|
|
InternalError(12345);
|
|
end;
|
|
GotPlus:=(prevasmtoken=AS_PLUS) or
|
|
(prevasmtoken=AS_MINUS);
|
|
if GotPlus then
|
|
negative:=(prevasmtoken=AS_MINUS);
|
|
end;
|
|
|
|
AS_LPAREN :
|
|
begin
|
|
consume(AS_LPAREN);
|
|
oper.InitRef;
|
|
if actasmtoken=AS_REGISTER then
|
|
oper.opr.ref.base:=actasmregister;
|
|
consume(AS_REGISTER);
|
|
consume(AS_RPAREN);
|
|
gotplus:=false;
|
|
end;
|
|
|
|
AS_RELTYPE:
|
|
begin
|
|
{ Low or High part of a constant (or constant
|
|
memory location) }
|
|
oper.InitRef;
|
|
oper.opr.ref.refaddr:=actrel;
|
|
Consume(actasmtoken);
|
|
Consume(AS_LPAREN);
|
|
BuildConstSymbolExpression(false, true,false,l,tempstr,tempsymtyp);
|
|
if not assigned(oper.opr.ref.symbol) then
|
|
oper.opr.ref.symbol:=current_asmdata.RefAsmSymbol(tempstr,tempsymtyp)
|
|
else
|
|
Message(asmr_e_cant_have_multiple_relocatable_symbols);
|
|
case oper.opr.typ of
|
|
OPR_CONSTANT :
|
|
inc(oper.opr.val,l);
|
|
OPR_LOCAL :
|
|
inc(oper.opr.localsymofs,l);
|
|
OPR_REFERENCE :
|
|
inc(oper.opr.ref.offset,l);
|
|
else
|
|
internalerror(200309202);
|
|
end;
|
|
Consume(AS_RPAREN);
|
|
gotplus:=false;
|
|
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);
|
|
end
|
|
else
|
|
{ Check for label }
|
|
if SearchLabel(actasmpattern,hl,false) then
|
|
begin
|
|
Consume(AS_ID);
|
|
AddLabelOperand(hl);
|
|
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 not oper.SetupVar(expr,false) then
|
|
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 actasmtoken=AS_DOT then
|
|
MaybeRecordOffset;
|
|
{ add a constant expression? }
|
|
if (actasmtoken=AS_PLUS) then
|
|
begin
|
|
l:=BuildConstExpression(true,false);
|
|
case oper.opr.typ of
|
|
OPR_CONSTANT :
|
|
inc(oper.opr.val,l);
|
|
OPR_LOCAL :
|
|
inc(oper.opr.localsymofs,l);
|
|
OPR_REFERENCE :
|
|
inc(oper.opr.ref.offset,l);
|
|
else
|
|
internalerror(200309202);
|
|
end;
|
|
end
|
|
end;
|
|
gotplus:=false;
|
|
end;
|
|
|
|
|
|
AS_REGISTER: { Register, a variable reference or a constant reference }
|
|
Begin
|
|
{ save the type of register used. }
|
|
tempreg:=actasmregister;
|
|
Consume(AS_REGISTER);
|
|
begin
|
|
if (oper.opr.typ<>OPR_NONE) then
|
|
Message(asmr_e_invalid_operand_type);
|
|
oper.opr.typ:=OPR_REGISTER;
|
|
oper.opr.reg:=tempreg;
|
|
end;
|
|
gotplus:=false;
|
|
end;
|
|
|
|
AS_END,
|
|
AS_SEPARATOR,
|
|
AS_COMMA:
|
|
break;
|
|
else
|
|
Begin
|
|
Message(asmr_e_syn_operand);
|
|
Consume(actasmtoken);
|
|
end;
|
|
end; { end case }
|
|
until false;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
TMipsReader
|
|
*****************************************************************************}
|
|
|
|
procedure TMipsReader.BuildOpCode(instr : TInstruction);
|
|
var
|
|
operandnum : longint;
|
|
Begin
|
|
{ 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;
|
|
end;
|
|
|
|
{ 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
|
|
begin
|
|
{ condition operands doesn't set the operand but write to the
|
|
condition field of the instruction
|
|
}
|
|
if instr.Operands[operandnum].opr.typ<>OPR_NONE then
|
|
Inc(operandnum);
|
|
end;
|
|
Consume(AS_COMMA);
|
|
end;
|
|
AS_SEPARATOR,
|
|
AS_END : { End of asm operands for this opcode }
|
|
begin
|
|
break;
|
|
end;
|
|
else
|
|
BuildOperand(instr.Operands[operandnum] as TOperand);
|
|
end; { end case }
|
|
until false;
|
|
if (operandnum=1) and (instr.Operands[operandnum].opr.typ=OPR_NONE) then
|
|
dec(operandnum);
|
|
instr.Ops:=operandnum;
|
|
end;
|
|
|
|
|
|
function TMipsReader.is_asmopcode(const s: string):boolean;
|
|
var
|
|
cond:TAsmCond;
|
|
hs:string;
|
|
Begin
|
|
is_asmopcode:=false;
|
|
|
|
{ clear op code and condition }
|
|
actopcode:=A_None;
|
|
actcondition:=C_None;
|
|
|
|
{ Search opcodes }
|
|
actopcode:=tasmop(PtrUInt(iasmops.Find(s)));
|
|
if actopcode<>A_NONE then
|
|
begin
|
|
actasmtoken:=AS_OPCODE;
|
|
result:=TRUE;
|
|
exit;
|
|
end;
|
|
|
|
{ not found, check branch instructions }
|
|
if (Upcase(s[1])='B') then
|
|
begin
|
|
{ we can search here without an extra table which is sorted by string length
|
|
because we take the whole remaining string without the leading B }
|
|
actopcode := A_BC;
|
|
hs:=lower(copy(s,2,maxint));
|
|
for cond:=low(TAsmCond) to high(TAsmCond) do
|
|
if (hs=Cond2Str[cond]) then
|
|
begin
|
|
actasmtoken:=AS_OPCODE;
|
|
actcondition:=cond;
|
|
is_asmopcode:=true;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TMipsReader.handleopcode;
|
|
var
|
|
instr : TInstruction;
|
|
begin
|
|
instr:=TInstruction.Create(TOperand);
|
|
BuildOpcode(instr);
|
|
with instr do
|
|
begin
|
|
condition := actcondition;
|
|
{ Coprocessor-related instructions have operands referring to both coprocessor registers
|
|
and general-purpose ones. The input representation "$<number>" is the same for both,
|
|
but symbolic names must not be used for non-GPRs on output. }
|
|
if (opcode in [A_MTC0,A_MFC0,A_CFC1,A_CTC1{,A_CFC2,A_CTC2}]) then
|
|
begin
|
|
{ operands are 1-based here }
|
|
if (ops<2) or (operands[2].opr.typ<>OPR_REGISTER) then
|
|
message(asmr_e_syn_operand);
|
|
operands[2].opr.reg:=newreg(R_SPECIALREGISTER,getsupreg(operands[2].opr.reg),R_SUBNONE);
|
|
end;
|
|
ConcatInstruction(curlist);
|
|
Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
{*****************************************************************************
|
|
Initialize
|
|
*****************************************************************************}
|
|
|
|
const
|
|
asmmode_mips_att_info : tasmmodeinfo =
|
|
(
|
|
id : asmmode_standard;
|
|
idtxt : 'GAS';
|
|
casmreader : TMipsReader;
|
|
);
|
|
|
|
initialization
|
|
RegisterAsmMode(asmmode_mips_att_info);
|
|
end.
|