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