1 {
2     Copyright (c) 2003 by Florian Klaempfl
3 
4     This unit implements an asm for the ARM
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 
20  ****************************************************************************
21 }
22 { This unit implements the GNU Assembler writer for the ARM
23 }
24 
25 unit agarmgas;
26 
27 {$i fpcdefs.inc}
28 
29   interface
30 
31     uses
32        globtype,systems,
33        aasmtai,
34        assemble,aggas,
35        cpubase,cpuinfo;
36 
37     type
38       TARMGNUAssembler=class(TGNUassembler)
39         constructor CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); override;
MakeCmdLinenull40         function MakeCmdLine: TCmdStr; override;
41         procedure WriteExtraHeader; override;
42       end;
43 
44       TArmInstrWriter=class(TCPUInstrWriter)
45         unified_syntax: boolean;
46 
47         procedure WriteInstruction(hp : tai);override;
48       end;
49 
50       TArmAppleGNUAssembler=class(TAppleGNUassembler)
51         constructor CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean); override;
MakeCmdLinenull52         function MakeCmdLine: TCmdStr; override;
53         procedure WriteExtraHeader; override;
54       end;
55 
56 
57     const
58       gas_shiftmode2str : array[tshiftmode] of string[3] = (
59         '','lsl','lsr','asr','ror','rrx');
60 
61     const
62       cputype_to_gas_march : array[tcputype] of string = (
63         '', // cpu_none
64         'armv3',
65         'armv4',
66         'armv4t',
67         'armv5',
68         'armv5t',
69         'armv5te',
70         'armv5tej',
71         'armv6',
72         'armv6k',
73         'armv6t2',
74         'armv6z',
75         'armv6-m',
76         'armv7',
77         'armv7-a',
78         'armv7-r',
79         'armv7-m',
80         'armv7e-m');
81 
82   implementation
83 
84     uses
85        cutils,globals,verbose,
86        aasmcpu,
87        itcpugas,
88        cgbase,cgutils;
89 
90 {****************************************************************************}
91 {                         GNU Arm Assembler writer                           }
92 {****************************************************************************}
93 
94     constructor TArmGNUAssembler.CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean);
95       begin
96         inherited;
97         InstrWriter := TArmInstrWriter.create(self);
98         if GenerateThumb2Code then
99           TArmInstrWriter(InstrWriter).unified_syntax:=true;
100       end;
101 
102 
TArmGNUAssembler.MakeCmdLinenull103     function TArmGNUAssembler.MakeCmdLine: TCmdStr;
104       begin
105         result:=inherited MakeCmdLine;
106         if (current_settings.fputype = fpu_soft) then
107           result:='-mfpu=softvfp '+result;
108         if (current_settings.fputype = fpu_fpa) then
109           result:='-mfpu=fpa '+result;
110         if (current_settings.fputype = fpu_fpa10) then
111           result:='-mfpu=fpa10 '+result;
112         if (current_settings.fputype = fpu_fpa11) then
113           result:='-mfpu=fpa11 '+result;
114         if (current_settings.fputype = fpu_vfpv2) then
115           result:='-mfpu=vfpv2 '+result;
116         if (current_settings.fputype = fpu_vfpv3) then
117           result:='-mfpu=vfpv3 '+result;
118         if (current_settings.fputype = fpu_vfpv3_d16) then
119           result:='-mfpu=vfpv3-d16 '+result;
120         if (current_settings.fputype = fpu_fpv4_s16) then
121           result:='-mfpu=fpv4-sp-d16 '+result;
122         if (current_settings.fputype = fpu_vfpv4) then
123           result:='-mfpu=vfpv4 '+result;
124 
125         if GenerateThumb2Code then
126           result:='-march='+cputype_to_gas_march[current_settings.cputype]+' -mthumb -mthumb-interwork '+result
127         else if GenerateThumbCode then
128           result:='-march='+cputype_to_gas_march[current_settings.cputype]+' -mthumb -mthumb-interwork '+result
129         else
130           result:='-march='+cputype_to_gas_march[current_settings.cputype]+' '+result;
131 
132         if target_info.abi = abi_eabihf then
133           { options based on what gcc uses on debian armhf }
134           result:='-mfloat-abi=hard -meabi=5 '+result;
135       end;
136 
137     procedure TArmGNUAssembler.WriteExtraHeader;
138       begin
139         inherited WriteExtraHeader;
140         if TArmInstrWriter(InstrWriter).unified_syntax then
141           writer.AsmWriteLn(#9'.syntax unified');
142       end;
143 
144 {****************************************************************************}
145 {                      GNU/Apple ARM Assembler writer                        }
146 {****************************************************************************}
147 
148     constructor TArmAppleGNUAssembler.CreateWithWriter(info: pasminfo; wr: TExternalAssemblerOutputFile; freewriter, smart: boolean);
149       begin
150         inherited;
151         InstrWriter := TArmInstrWriter.create(self);
152         TArmInstrWriter(InstrWriter).unified_syntax:=true;
153       end;
154 
155 
TArmAppleGNUAssembler.MakeCmdLinenull156     function TArmAppleGNUAssembler.MakeCmdLine: TCmdStr;
157       begin
158         result:=inherited MakeCmdLine;
159 	if (asminfo^.id in [as_clang_gas,as_clang_asdarwin]) then
160           begin
161             if fputypestrllvm[current_settings.fputype] <> '' then
162               result:='-m'+fputypestrllvm[current_settings.fputype]+' '+result;
163             { Apple arm always uses softfp floating point ABI }
164             result:='-mfloat-abi=softfp '+result;
165           end;
166       end;
167 
168     procedure TArmAppleGNUAssembler.WriteExtraHeader;
169       begin
170         inherited WriteExtraHeader;
171         if TArmInstrWriter(InstrWriter).unified_syntax then
172           writer.AsmWriteLn(#9'.syntax unified');
173       end;
174 
175 
176 {****************************************************************************}
177 {                  Helper routines for Instruction Writer                    }
178 {****************************************************************************}
179 
getreferencestringnull180     function getreferencestring(var ref : treference) : string;
181       var
182         s : string;
183       begin
184          with ref do
185           begin
186 {$ifdef extdebug}
187             // if base=NR_NO then
188             //   internalerror(200308292);
189 
190             // if ((index<>NR_NO) or (shiftmode<>SM_None)) and ((offset<>0) or (symbol<>nil)) then
191             //   internalerror(200308293);
192 {$endif extdebug}
193 
194             if assigned(symbol) then
195               begin
196                 if (base<>NR_NO) and not(is_pc(base)) then
197                   internalerror(200309011);
198                 s:=symbol.name;
199                 if offset<>0 then
200                   s:=s+tostr_with_plus(offset);
201                 if refaddr=addr_pic then
202                   s:=s+'(PLT)';
203               end
204             else
205               begin
206                 s:='['+gas_regname(base);
207                 if addressmode=AM_POSTINDEXED then
208                   s:=s+']';
209                 if index<>NR_NO then
210                   begin
211                      if signindex<0 then
212                        s:=s+', -'
213                      else
214                        s:=s+', ';
215 
216                      s:=s+gas_regname(index);
217 
218                      {RRX always rotates by 1 bit and does not take an imm}
219                      if shiftmode = SM_RRX then
220                        s:=s+', rrx'
221                      else if shiftmode <> SM_None then
222                        s:=s+', '+gas_shiftmode2str[shiftmode]+' #'+tostr(shiftimm);
223                   end
224                 else if offset<>0 then
225                   s:=s+', #'+tostr(offset);
226 
227                 case addressmode of
228                   AM_OFFSET:
229                     s:=s+']';
230                   AM_PREINDEXED:
231                     s:=s+']!';
232                 end;
233               end;
234 
235           end;
236         getreferencestring:=s;
237       end;
238 
getopstrnull239     function getopstr(const o:toper) : string;
240       var
241         hs : string;
242         first : boolean;
243         r, rs : tsuperregister;
244       begin
245         case o.typ of
246           top_reg:
247             getopstr:=gas_regname(o.reg);
248           top_shifterop:
249             begin
250               {RRX is special, it only rotates by 1 and does not take any shiftervalue}
251               if o.shifterop^.shiftmode=SM_RRX then
252                 getopstr:='rrx'
253               else if (o.shifterop^.rs<>NR_NO) and (o.shifterop^.shiftimm=0) then
254                 getopstr:=gas_shiftmode2str[o.shifterop^.shiftmode]+' '+gas_regname(o.shifterop^.rs)
255               else if (o.shifterop^.rs=NR_NO) then
256                 getopstr:=gas_shiftmode2str[o.shifterop^.shiftmode]+' #'+tostr(o.shifterop^.shiftimm)
257               else internalerror(200308282);
258             end;
259           top_const:
260             getopstr:='#'+tostr(longint(o.val));
261           top_regset:
262             begin
263               getopstr:='{';
264               first:=true;
265               if R_SUBFS=o.subreg then
266                 begin
267                   for r:=0 to 31 do // S0 to S31
268                     if r in o.regset^ then
269                       begin
270                         if not(first) then
271                           getopstr:=getopstr+',';
272                         if odd(r) then
273                           rs:=(r shr 1)+RS_S1
274                         else
275                           rs:=(r shr 1)+RS_S0;
276                         getopstr:=getopstr+gas_regname(newreg(o.regtyp,rs,o.subreg));
277                         first:=false;
278                       end;
279                 end
280               else if R_SUBFD=o.subreg then
281                 begin
282                   for r:=0 to 31 do
283                     if r in o.regset^ then
284                       begin
285                         if not(first) then
286                           getopstr:=getopstr+',';
287                         rs:=r+RS_D0;
288                         getopstr:=getopstr+gas_regname(newreg(o.regtyp,rs,o.subreg));
289                         first:=false;
290                       end;
291                 end
292               else
293                 begin
294                   for r:=RS_R0 to RS_R15 do
295                     if r in o.regset^ then
296                       begin
297                         if not(first) then
298                           getopstr:=getopstr+',';
299                         getopstr:=getopstr+gas_regname(newreg(o.regtyp,r,o.subreg));
300                         first:=false;
301                       end;
302                 end;
303               getopstr:=getopstr+'}';
304               if o.usermode then
305                 getopstr:=getopstr+'^';
306             end;
307           top_conditioncode:
308             getopstr:=cond2str[o.cc];
309           top_modeflags:
310             begin
311               getopstr:='';
312               if mfA in o.modeflags then getopstr:=getopstr+'a';
313               if mfI in o.modeflags then getopstr:=getopstr+'i';
314               if mfF in o.modeflags then getopstr:=getopstr+'f';
315             end;
316           top_ref:
317             if o.ref^.refaddr=addr_full then
318               begin
319                 hs:=o.ref^.symbol.name;
320                 if o.ref^.offset>0 then
321                  hs:=hs+'+'+tostr(o.ref^.offset)
322                 else
323                  if o.ref^.offset<0 then
324                   hs:=hs+tostr(o.ref^.offset);
325                 getopstr:=hs;
326               end
327             else
328               getopstr:=getreferencestring(o.ref^);
329           top_specialreg:
330             begin
331               getopstr:=gas_regname(o.specialreg);
332               if o.specialflags<>[] then
333                 begin
334                   getopstr:=getopstr+'_';
335                   if srC in o.specialflags then getopstr:=getopstr+'c';
336                   if srX in o.specialflags then getopstr:=getopstr+'x';
337                   if srF in o.specialflags then getopstr:=getopstr+'f';
338                   if srS in o.specialflags then getopstr:=getopstr+'s';
339                 end;
340             end;
341           top_realconst:
342             begin
343               str(o.val_real,Result);
344               Result:='#'+Result;
345             end
346           else
347             internalerror(2002070604);
348         end;
349       end;
350 
351 
352     Procedure TArmInstrWriter.WriteInstruction(hp : tai);
353     var op: TAsmOp;
354         postfix,s: string;
355         i: byte;
356         sep: string[3];
357     begin
358       op:=taicpu(hp).opcode;
359       postfix:='';
360       if GenerateThumb2Code then
361         begin
362           if taicpu(hp).wideformat then
363             postfix:='.w';
364         end;
365       if unified_syntax then
366         begin
367           if taicpu(hp).ops = 0 then
368             s:=#9+gas_op2str[op]+cond2str[taicpu(hp).condition]+oppostfix2str[taicpu(hp).oppostfix]
369           else if taicpu(hp).oppostfix in [PF_8..PF_U32F64] then
370             s:=#9+gas_op2str[op]+cond2str[taicpu(hp).condition]+oppostfix2str[taicpu(hp).oppostfix]
371           else
372             s:=#9+gas_op2str[op]+oppostfix2str[taicpu(hp).oppostfix]+cond2str[taicpu(hp).condition]+postfix; // Conditional infixes are deprecated in unified syntax
373         end
374       else
375         s:=#9+gas_op2str[op]+cond2str[taicpu(hp).condition]+oppostfix2str[taicpu(hp).oppostfix];
376       if taicpu(hp).ops<>0 then
377         begin
378           sep:=#9;
379           for i:=0 to taicpu(hp).ops-1 do
380             begin
381                // debug code
382                // writeln(s);
383                // writeln(taicpu(hp).fileinfo.line);
384 
385                { LDM and STM use references as first operand but they are written like a register }
386                if (i=0) and (op in [A_LDM,A_STM,A_FSTM,A_FLDM,A_VSTM,A_VLDM,A_SRS,A_RFE]) then
387                  begin
388                    case taicpu(hp).oper[0]^.typ of
389                      top_ref:
390                        begin
391                          s:=s+sep+gas_regname(taicpu(hp).oper[0]^.ref^.index);
392                          if taicpu(hp).oper[0]^.ref^.addressmode=AM_PREINDEXED then
393                            s:=s+'!';
394                        end;
395                      top_reg:
396                        s:=s+sep+gas_regname(taicpu(hp).oper[0]^.reg);
397                      else
398                        internalerror(200311292);
399                    end;
400                  end
401                { register count of SFM and LFM is written without # }
402                else if (i=1) and (op in [A_SFM,A_LFM]) then
403                  begin
404                    case taicpu(hp).oper[1]^.typ of
405                      top_const:
406                        s:=s+sep+tostr(taicpu(hp).oper[1]^.val);
407                      else
408                        internalerror(200311292);
409                    end;
410                  end
411                else
412                  s:=s+sep+getopstr(taicpu(hp).oper[i]^);
413 
414                sep:=',';
415             end;
416         end;
417       owner.writer.AsmWriteLn(s);
418     end;
419 
420 
421     const
422        as_arm_gas_info : tasminfo =
423           (
424             id     : as_gas;
425 
426             idtxt  : 'AS';
427             asmbin : 'as';
428             asmcmd : '-o $OBJ $EXTRAOPT $ASM';
429             supported_targets : [system_arm_linux,system_arm_netbsd,system_arm_wince,system_arm_gba,system_arm_palmos,system_arm_nds,
430                                  system_arm_embedded,system_arm_symbian,system_arm_android,system_arm_aros];
431             flags : [af_needar,af_smartlink_sections];
432             labelprefix : '.L';
433             comment : '# ';
434             dollarsign: '$';
435           );
436 
437        as_arm_gas_darwin_info : tasminfo =
438           (
439             id     : as_darwin;
440             idtxt  : 'AS-DARWIN';
441             asmbin : 'as';
442             asmcmd : '-o $OBJ $EXTRAOPT $ASM -arch $ARCH';
443             supported_targets : [system_arm_ios];
444             flags : [af_needar,af_smartlink_sections,af_supports_dwarf,af_stabs_use_function_absolute_addresses];
445             labelprefix : 'L';
446             comment : '# ';
447             dollarsign: '$';
448           );
449 
450 
451        as_arm_clang_darwin_info : tasminfo =
452           (
453             id     : as_clang_asdarwin;
454             idtxt  : 'CLANG';
455             asmbin : 'clang';
456             asmcmd : '-x assembler -c -target $TRIPLET -o $OBJ $EXTRAOPT -x assembler $ASM';
457             supported_targets : [system_arm_ios];
458             flags : [af_needar,af_smartlink_sections,af_supports_dwarf,af_llvm];
459             labelprefix : 'L';
460             comment : '# ';
461             dollarsign: '$';
462           );
463 
464 
465 begin
466   RegisterAssembler(as_arm_gas_info,TARMGNUAssembler);
467   RegisterAssembler(as_arm_gas_darwin_info,TArmAppleGNUAssembler);
468   RegisterAssembler(as_arm_clang_darwin_info,TArmAppleGNUAssembler);
469 end.
470