ncgmem.pas 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. {
  2. Copyright (c) 1998-2002 by Florian Klaempfl
  3. Generate assembler for memory related nodes which are
  4. the same for all (most?) processors
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************
  17. }
  18. unit ncgmem;
  19. {$i fpcdefs.inc}
  20. interface
  21. uses
  22. globtype,cgbase,cpuinfo,cpubase,
  23. node,nmem;
  24. type
  25. tcgloadvmtaddrnode = class(tloadvmtaddrnode)
  26. procedure pass_generate_code;override;
  27. end;
  28. tcgloadparentfpnode = class(tloadparentfpnode)
  29. procedure pass_generate_code;override;
  30. end;
  31. tcgaddrnode = class(taddrnode)
  32. procedure pass_generate_code;override;
  33. end;
  34. tcgderefnode = class(tderefnode)
  35. procedure pass_generate_code;override;
  36. end;
  37. tcgsubscriptnode = class(tsubscriptnode)
  38. procedure pass_generate_code;override;
  39. end;
  40. tcgwithnode = class(twithnode)
  41. procedure pass_generate_code;override;
  42. end;
  43. tcgvecnode = class(tvecnode)
  44. function get_mul_size : aint;
  45. private
  46. procedure rangecheck_array;
  47. protected
  48. {# This routine is used to calculate the address of the reference.
  49. On entry reg contains the index in the array,
  50. and l contains the size of each element in the array.
  51. This routine should update location.reference correctly,
  52. so it points to the correct address.
  53. }
  54. procedure update_reference_reg_mul(maybe_const_reg:tregister;l:aint);virtual;
  55. procedure update_reference_reg_packed(maybe_const_reg:tregister;l:aint);virtual;
  56. procedure second_wideansistring;virtual;
  57. procedure second_dynamicarray;virtual;
  58. public
  59. procedure pass_generate_code;override;
  60. end;
  61. implementation
  62. uses
  63. systems,
  64. cutils,cclasses,verbose,globals,constexp,
  65. symconst,symdef,symsym,symtable,defutil,paramgr,
  66. aasmbase,aasmtai,aasmdata,
  67. procinfo,pass_2,parabase,
  68. pass_1,nld,ncon,nadd,nutils,
  69. cgutils,cgobj,
  70. tgobj,ncgutil,objcgutl
  71. ;
  72. {*****************************************************************************
  73. TCGLOADVMTADDRNODE
  74. *****************************************************************************}
  75. procedure tcgloadvmtaddrnode.pass_generate_code;
  76. var
  77. href : treference;
  78. pool : THashSet;
  79. entry : PHashSetItem;
  80. begin
  81. location_reset(location,LOC_REGISTER,OS_ADDR);
  82. if (left.nodetype=typen) then
  83. begin
  84. location.register:=cg.getaddressregister(current_asmdata.CurrAsmList);
  85. if not is_objcclass(left.resultdef) then
  86. begin
  87. reference_reset_symbol(href,
  88. current_asmdata.RefAsmSymbol(tobjectdef(tclassrefdef(resultdef).pointeddef).vmt_mangledname),0,
  89. sizeof(pint));
  90. cg.a_loadaddr_ref_reg(current_asmdata.CurrAsmList,href,location.register);
  91. end
  92. else
  93. begin
  94. if current_asmdata.ConstPools[sp_objcclassnamerefs]=nil then
  95. current_asmdata.ConstPools[sp_objcclassnamerefs]:=THashSet.Create(64, True, False);
  96. pool:=current_asmdata.ConstPools[sp_objcclassnamerefs];
  97. entry:=pool.FindOrAdd(@tobjectdef(left.resultdef).objextname^[1],length(tobjectdef(left.resultdef).objextname^));
  98. if (target_info.system in systems_objc_nfabi) then
  99. begin
  100. { find/add necessary classref/classname pool entries }
  101. objcfinishclassrefnfpoolentry(entry,tobjectdef(left.resultdef));
  102. end
  103. else
  104. begin
  105. { find/add necessary classref/classname pool entries }
  106. objcfinishstringrefpoolentry(entry,sp_objcclassnames,sec_objc_cls_refs,sec_objc_class_names);
  107. end;
  108. reference_reset_symbol(href,tasmlabel(entry^.Data),0,sizeof(pint));
  109. cg.a_load_ref_reg(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,href,location.register);
  110. end;
  111. end
  112. else
  113. begin
  114. { left contains self, load vmt from self }
  115. secondpass(left);
  116. gen_load_vmt_register(current_asmdata.CurrAsmList,tobjectdef(left.resultdef),left.location,location.register);
  117. end;
  118. end;
  119. {*****************************************************************************
  120. TCGLOADPARENTFPNODE
  121. *****************************************************************************}
  122. procedure tcgloadparentfpnode.pass_generate_code;
  123. var
  124. currpi : tprocinfo;
  125. hsym : tparavarsym;
  126. href : treference;
  127. begin
  128. if (current_procinfo.procdef.parast.symtablelevel=parentpd.parast.symtablelevel) then
  129. begin
  130. location_reset(location,LOC_REGISTER,OS_ADDR);
  131. location.register:=current_procinfo.framepointer;
  132. end
  133. else
  134. begin
  135. currpi:=current_procinfo;
  136. location_reset(location,LOC_REGISTER,OS_ADDR);
  137. location.register:=cg.getaddressregister(current_asmdata.CurrAsmList);
  138. { load framepointer of current proc }
  139. hsym:=tparavarsym(currpi.procdef.parast.Find('parentfp'));
  140. if not assigned(hsym) then
  141. internalerror(200309281);
  142. cg.a_load_loc_reg(current_asmdata.CurrAsmList,OS_ADDR,hsym.localloc,location.register);
  143. { walk parents }
  144. while (currpi.procdef.owner.symtablelevel>parentpd.parast.symtablelevel) do
  145. begin
  146. currpi:=currpi.parent;
  147. if not assigned(currpi) then
  148. internalerror(200311201);
  149. hsym:=tparavarsym(currpi.procdef.parast.Find('parentfp'));
  150. if not assigned(hsym) then
  151. internalerror(200309282);
  152. if hsym.localloc.loc<>LOC_REFERENCE then
  153. internalerror(200309283);
  154. reference_reset_base(href,location.register,hsym.localloc.reference.offset,sizeof(pint));
  155. cg.a_load_ref_reg(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,href,location.register);
  156. end;
  157. end;
  158. end;
  159. {*****************************************************************************
  160. TCGADDRNODE
  161. *****************************************************************************}
  162. procedure tcgaddrnode.pass_generate_code;
  163. begin
  164. secondpass(left);
  165. location_reset(location,LOC_REGISTER,OS_ADDR);
  166. location.register:=cg.getaddressregister(current_asmdata.CurrAsmList);
  167. if not(left.location.loc in [LOC_REFERENCE,LOC_CREFERENCE]) then
  168. { on x86_64-win64, array of chars can be returned in registers, however,
  169. when passing these arrays to other functions, the compiler wants to take
  170. the address of the array so when the addrnode has been created internally,
  171. we have to force the data into memory, see also tw14388.pp
  172. }
  173. if nf_internal in flags then
  174. location_force_mem(current_asmdata.CurrAsmList,left.location)
  175. else
  176. internalerror(2006111510);
  177. cg.a_loadaddr_ref_reg(current_asmdata.CurrAsmList,left.location.reference,location.register);
  178. end;
  179. {*****************************************************************************
  180. TCGDEREFNODE
  181. *****************************************************************************}
  182. procedure tcgderefnode.pass_generate_code;
  183. var
  184. paraloc1 : tcgpara;
  185. begin
  186. secondpass(left);
  187. { assume natural alignment, except for packed records }
  188. if not(resultdef.typ in [recorddef,objectdef]) or
  189. (tabstractrecordsymtable(tabstractrecorddef(resultdef).symtable).usefieldalignment<>1) then
  190. location_reset_ref(location,LOC_REFERENCE,def_cgsize(resultdef),resultdef.alignment)
  191. else
  192. location_reset_ref(location,LOC_REFERENCE,def_cgsize(resultdef),1);
  193. if not(left.location.loc in [LOC_CREGISTER,LOC_REGISTER,LOC_CREFERENCE,LOC_REFERENCE,LOC_CONSTANT]) then
  194. location_force_reg(current_asmdata.CurrAsmList,left.location,OS_ADDR,true);
  195. case left.location.loc of
  196. LOC_CREGISTER,
  197. LOC_REGISTER:
  198. begin
  199. maybechangeloadnodereg(current_asmdata.CurrAsmList,left,true);
  200. {$ifdef cpu_uses_separate_address_registers}
  201. if getregtype(left.location.register)<>R_ADDRESSREGISTER then
  202. begin
  203. location.reference.base := cg.getaddressregister(current_asmdata.CurrAsmList);
  204. cg.a_load_reg_reg(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,left.location.register,
  205. location.reference.base);
  206. end
  207. else
  208. {$endif}
  209. location.reference.base := left.location.register;
  210. end;
  211. LOC_CREFERENCE,
  212. LOC_REFERENCE:
  213. begin
  214. location.reference.base:=cg.getaddressregister(current_asmdata.CurrAsmList);
  215. cg.a_load_loc_reg(current_asmdata.CurrAsmList,OS_ADDR,left.location,location.reference.base);
  216. end;
  217. LOC_CONSTANT:
  218. begin
  219. location.reference.offset:=left.location.value;
  220. end;
  221. else
  222. internalerror(200507031);
  223. end;
  224. if (cs_use_heaptrc in current_settings.globalswitches) and
  225. (cs_checkpointer in current_settings.localswitches) and
  226. not(cs_compilesystem in current_settings.moduleswitches) and
  227. not(tpointerdef(left.resultdef).is_far) and
  228. not(nf_no_checkpointer in flags) and
  229. { can be NR_NO in case of LOC_CONSTANT }
  230. (location.reference.base<>NR_NO) then
  231. begin
  232. paraloc1.init;
  233. paramanager.getintparaloc(pocall_default,1,paraloc1);
  234. paramanager.allocparaloc(current_asmdata.CurrAsmList,paraloc1);
  235. cg.a_param_reg(current_asmdata.CurrAsmList, OS_ADDR,location.reference.base,paraloc1);
  236. paramanager.freeparaloc(current_asmdata.CurrAsmList,paraloc1);
  237. paraloc1.done;
  238. cg.allocallcpuregisters(current_asmdata.CurrAsmList);
  239. cg.a_call_name(current_asmdata.CurrAsmList,'FPC_CHECKPOINTER',false);
  240. cg.deallocallcpuregisters(current_asmdata.CurrAsmList);
  241. end;
  242. end;
  243. {*****************************************************************************
  244. TCGSUBSCRIPTNODE
  245. *****************************************************************************}
  246. procedure tcgsubscriptnode.pass_generate_code;
  247. var
  248. sym: tasmsymbol;
  249. paraloc1 : tcgpara;
  250. hreg : tregister;
  251. tmpref: treference;
  252. sref: tsubsetreference;
  253. begin
  254. secondpass(left);
  255. if codegenerror then
  256. exit;
  257. paraloc1.init;
  258. { classes and interfaces must be dereferenced implicitly }
  259. if is_class_or_interface_or_dispinterface_or_objc(left.resultdef) then
  260. begin
  261. { the contents of a class are aligned to a sizeof(pointer) }
  262. location_reset_ref(location,LOC_REFERENCE,def_cgsize(resultdef),sizeof(pint));
  263. case left.location.loc of
  264. LOC_CREGISTER,
  265. LOC_REGISTER:
  266. begin
  267. {$ifdef cpu_uses_separate_address_registers}
  268. if getregtype(left.location.register)<>R_ADDRESSREGISTER then
  269. begin
  270. location.reference.base:=rg.getaddressregister(current_asmdata.CurrAsmList);
  271. cg.a_load_reg_reg(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,
  272. left.location.register,location.reference.base);
  273. end
  274. else
  275. {$endif}
  276. location.reference.base := left.location.register;
  277. end;
  278. LOC_CREFERENCE,
  279. LOC_REFERENCE:
  280. begin
  281. location.reference.base:=cg.getaddressregister(current_asmdata.CurrAsmList);
  282. cg.a_load_loc_reg(current_asmdata.CurrAsmList,OS_ADDR,left.location,location.reference.base);
  283. end;
  284. LOC_CONSTANT:
  285. begin
  286. { can happen with @classtype(pointerconst).field }
  287. location.reference.offset:=left.location.value;
  288. end;
  289. else
  290. internalerror(2009092401);
  291. end;
  292. { implicit deferencing }
  293. if (cs_use_heaptrc in current_settings.globalswitches) and
  294. (cs_checkpointer in current_settings.localswitches) and
  295. not(cs_compilesystem in current_settings.moduleswitches) then
  296. begin
  297. paramanager.getintparaloc(pocall_default,1,paraloc1);
  298. paramanager.allocparaloc(current_asmdata.CurrAsmList,paraloc1);
  299. cg.a_param_reg(current_asmdata.CurrAsmList, OS_ADDR,location.reference.base,paraloc1);
  300. paramanager.freeparaloc(current_asmdata.CurrAsmList,paraloc1);
  301. cg.allocallcpuregisters(current_asmdata.CurrAsmList);
  302. cg.a_call_name(current_asmdata.CurrAsmList,'FPC_CHECKPOINTER',false);
  303. cg.deallocallcpuregisters(current_asmdata.CurrAsmList);
  304. end;
  305. end
  306. else if is_interfacecom(left.resultdef) then
  307. begin
  308. location_reset_ref(location,LOC_REFERENCE,def_cgsize(resultdef),sizeof(pint));
  309. tg.GetTempTyped(current_asmdata.CurrAsmList,left.resultdef,tt_normal,location.reference);
  310. cg.a_load_loc_ref(current_asmdata.CurrAsmList,OS_ADDR,left.location,location.reference);
  311. { implicit deferencing also for interfaces }
  312. if (cs_use_heaptrc in current_settings.globalswitches) and
  313. (cs_checkpointer in current_settings.localswitches) and
  314. not(cs_compilesystem in current_settings.moduleswitches) then
  315. begin
  316. paramanager.getintparaloc(pocall_default,1,paraloc1);
  317. paramanager.allocparaloc(current_asmdata.CurrAsmList,paraloc1);
  318. cg.a_param_reg(current_asmdata.CurrAsmList, OS_ADDR,location.reference.base,paraloc1);
  319. paramanager.freeparaloc(current_asmdata.CurrAsmList,paraloc1);
  320. cg.allocallcpuregisters(current_asmdata.CurrAsmList);
  321. cg.a_call_name(current_asmdata.CurrAsmList,'FPC_CHECKPOINTER',false);
  322. cg.deallocallcpuregisters(current_asmdata.CurrAsmList);
  323. end;
  324. end
  325. else
  326. begin
  327. location_copy(location,left.location);
  328. { some abi's require that functions return (some) records in }
  329. { registers }
  330. case location.loc of
  331. LOC_REFERENCE,
  332. LOC_CREFERENCE:
  333. ;
  334. LOC_REGISTER,
  335. LOC_CREGISTER:
  336. begin
  337. if (left.resultdef.size > sizeof(pint)) then
  338. location_force_mem(current_asmdata.CurrAsmList,location)
  339. else
  340. begin
  341. if (left.location.loc = LOC_REGISTER) then
  342. location.loc := LOC_SUBSETREG
  343. else
  344. location.loc := LOC_CSUBSETREG;
  345. location.size:=def_cgsize(resultdef);
  346. location.sreg.subsetreg := left.location.register;
  347. location.sreg.subsetregsize := left.location.size;
  348. if not is_packed_record_or_object(left.resultdef) then
  349. begin
  350. if (target_info.endian = ENDIAN_BIG) then
  351. location.sreg.startbit := (tcgsize2size[location.sreg.subsetregsize] - tcgsize2size[location.size] - vs.fieldoffset) * 8
  352. else
  353. location.sreg.startbit := (vs.fieldoffset * 8);
  354. location.sreg.bitlen := tcgsize2size[location.size] * 8;
  355. end
  356. else
  357. begin
  358. location.sreg.bitlen := resultdef.packedbitsize;
  359. if (target_info.endian = ENDIAN_BIG) then
  360. location.sreg.startbit := (tcgsize2size[location.sreg.subsetregsize]*8 - location.sreg.bitlen) - vs.fieldoffset
  361. else
  362. location.sreg.startbit := vs.fieldoffset;
  363. end;
  364. end;
  365. end;
  366. LOC_SUBSETREG,
  367. LOC_CSUBSETREG:
  368. begin
  369. location.size:=def_cgsize(resultdef);
  370. if not is_packed_record_or_object(left.resultdef) then
  371. begin
  372. if (target_info.endian = ENDIAN_BIG) then
  373. inc(location.sreg.startbit, (left.resultdef.size - tcgsize2size[location.size] - vs.fieldoffset) * 8)
  374. else
  375. inc(location.sreg.startbit, vs.fieldoffset * 8);
  376. location.sreg.bitlen := tcgsize2size[location.size] * 8;
  377. end
  378. else
  379. begin
  380. location.sreg.bitlen := resultdef.packedbitsize;
  381. if (target_info.endian = ENDIAN_BIG) then
  382. inc(location.sreg.startbit, left.location.sreg.bitlen - location.sreg.bitlen - vs.fieldoffset)
  383. else
  384. inc(location.sreg.startbit, vs.fieldoffset);
  385. end;
  386. end;
  387. else
  388. internalerror(2006031901);
  389. end;
  390. end;
  391. if is_objc_class_or_protocol(left.resultdef) and
  392. (target_info.system in systems_objc_nfabi) then
  393. begin
  394. if (location.loc<>LOC_REFERENCE) or
  395. (location.reference.index<>NR_NO) then
  396. internalerror(2009092402);
  397. { the actual field offset is stored in memory (to solve the
  398. "fragile base class" problem: this way the layout of base
  399. classes can be changed without breaking programs compiled against
  400. earlier versions)
  401. }
  402. hreg:=cg.g_indirect_sym_load(current_asmdata.CurrAsmList,vs.mangledname,false);
  403. { TODO: clean up. g_indirect_sym_load cannot perform
  404. a plain load for targets that don't need an indirect load
  405. because it's also used in ncgld, but this is not very nice...
  406. }
  407. if (hreg=NR_NO) then
  408. begin
  409. sym:=current_asmdata.RefAsmSymbol(vs.mangledname);
  410. reference_reset_symbol(tmpref,sym,0,sizeof(pint));
  411. location.reference.index:=cg.getaddressregister(current_asmdata.CurrAsmList);
  412. end
  413. else
  414. begin
  415. reference_reset_base(tmpref,hreg,0,sizeof(pint));
  416. location.reference.index:=hreg;
  417. end;
  418. cg.a_load_ref_reg(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,tmpref,location.reference.index);
  419. { always packrecords C -> natural alignment }
  420. location.reference.alignment:=vs.vardef.alignment;
  421. end
  422. else if (location.loc in [LOC_REFERENCE,LOC_CREFERENCE]) then
  423. begin
  424. if not is_packed_record_or_object(left.resultdef) then
  425. begin
  426. inc(location.reference.offset,vs.fieldoffset);
  427. location.reference.alignment:=newalignment(location.reference.alignment,vs.fieldoffset);
  428. end
  429. else if (vs.fieldoffset mod 8 = 0) and
  430. (resultdef.packedbitsize mod 8 = 0) and
  431. { is different in case of e.g. packenum 2 and an enum }
  432. { which fits in 8 bits }
  433. (resultdef.size*8 = resultdef.packedbitsize) then
  434. begin
  435. inc(location.reference.offset,vs.fieldoffset div 8);
  436. location.reference.alignment:=newalignment(location.reference.alignment,vs.fieldoffset div 8);
  437. end
  438. else
  439. begin
  440. sref.ref:=location.reference;
  441. sref.ref.alignment:=1;
  442. sref.bitindexreg:=NR_NO;
  443. inc(sref.ref.offset,vs.fieldoffset div 8);
  444. sref.startbit:=vs.fieldoffset mod 8;
  445. sref.bitlen:=resultdef.packedbitsize;
  446. if (left.location.loc=LOC_REFERENCE) then
  447. location.loc:=LOC_SUBSETREF
  448. else
  449. location.loc:=LOC_CSUBSETREF;
  450. location.sref:=sref;
  451. end;
  452. { also update the size of the location }
  453. location.size:=def_cgsize(resultdef);
  454. end;
  455. paraloc1.done;
  456. end;
  457. {*****************************************************************************
  458. TCGWITHNODE
  459. *****************************************************************************}
  460. procedure tcgwithnode.pass_generate_code;
  461. begin
  462. location_reset(location,LOC_VOID,OS_NO);
  463. if assigned(left) then
  464. secondpass(left);
  465. end;
  466. {*****************************************************************************
  467. TCGVECNODE
  468. *****************************************************************************}
  469. function tcgvecnode.get_mul_size : aint;
  470. begin
  471. if nf_memindex in flags then
  472. get_mul_size:=1
  473. else
  474. begin
  475. if (left.resultdef.typ=arraydef) then
  476. if not is_packed_array(left.resultdef) then
  477. get_mul_size:=tarraydef(left.resultdef).elesize
  478. else
  479. get_mul_size:=tarraydef(left.resultdef).elepackedbitsize
  480. else
  481. get_mul_size:=resultdef.size;
  482. end
  483. end;
  484. { this routine must, like any other routine, not change the contents }
  485. { of base/index registers of references, as these may be regvars. }
  486. { The register allocator can coalesce one LOC_REGISTER being moved }
  487. { into another (as their live ranges won't overlap), but not a }
  488. { LOC_CREGISTER moved into a LOC_(C)REGISTER most of the time (as }
  489. { the live range of the LOC_CREGISTER will most likely overlap the }
  490. { the live range of the target LOC_(C)REGISTER) }
  491. { The passed register may be a LOC_CREGISTER as well. }
  492. procedure tcgvecnode.update_reference_reg_mul(maybe_const_reg:tregister;l:aint);
  493. var
  494. hreg: tregister;
  495. begin
  496. if l<>1 then
  497. begin
  498. hreg:=cg.getaddressregister(current_asmdata.CurrAsmList);
  499. cg.a_op_const_reg_reg(current_asmdata.CurrAsmList,OP_IMUL,OS_ADDR,l,maybe_const_reg,hreg);
  500. maybe_const_reg:=hreg;
  501. end;
  502. if location.reference.base=NR_NO then
  503. location.reference.base:=maybe_const_reg
  504. else if location.reference.index=NR_NO then
  505. location.reference.index:=maybe_const_reg
  506. else
  507. begin
  508. hreg:=cg.getaddressregister(current_asmdata.CurrAsmList);
  509. cg.a_loadaddr_ref_reg(current_asmdata.CurrAsmList,location.reference,hreg);
  510. reference_reset_base(location.reference,hreg,0,location.reference.alignment);
  511. { insert new index register }
  512. location.reference.index:=maybe_const_reg;
  513. end;
  514. { update alignment }
  515. if (location.reference.alignment=0) then
  516. internalerror(2009020704);
  517. location.reference.alignment:=newalignment(location.reference.alignment,l);
  518. end;
  519. { see remarks for tcgvecnode.update_reference_reg_mul above }
  520. procedure tcgvecnode.update_reference_reg_packed(maybe_const_reg:tregister;l:aint);
  521. var
  522. sref: tsubsetreference;
  523. offsetreg, hreg: tregister;
  524. alignpower: aint;
  525. temp : longint;
  526. begin
  527. { only orddefs are bitpacked. Even then we only need special code in }
  528. { case the bitpacked *byte size* is not a power of two, otherwise }
  529. { everything can be handled using the the regular array code. }
  530. if ((l mod 8) = 0) and
  531. (ispowerof2(l div 8,temp) or
  532. not is_ordinal(resultdef)) then
  533. begin
  534. update_reference_reg_mul(maybe_const_reg,l div 8);
  535. exit;
  536. end;
  537. if (l > 8*sizeof(aint)) then
  538. internalerror(200608051);
  539. sref.ref := location.reference;
  540. hreg := cg.getaddressregister(current_asmdata.CurrAsmList);
  541. cg.a_op_const_reg_reg(current_asmdata.CurrAsmList,OP_SUB,OS_INT,tarraydef(left.resultdef).lowrange,maybe_const_reg,hreg);
  542. cg.a_op_const_reg(current_asmdata.CurrAsmList,OP_IMUL,OS_INT,l,hreg);
  543. { keep alignment for index }
  544. sref.ref.alignment := left.resultdef.alignment;
  545. if not ispowerof2(sref.ref.alignment,temp) then
  546. internalerror(2006081201);
  547. alignpower:=temp;
  548. offsetreg := cg.getaddressregister(current_asmdata.CurrAsmList);
  549. cg.a_op_const_reg_reg(current_asmdata.CurrAsmList,OP_SHR,OS_ADDR,3+alignpower,hreg,offsetreg);
  550. cg.a_op_const_reg(current_asmdata.CurrAsmList,OP_SHL,OS_ADDR,alignpower,offsetreg);
  551. if (sref.ref.base = NR_NO) then
  552. sref.ref.base := offsetreg
  553. else if (sref.ref.index = NR_NO) then
  554. sref.ref.index := offsetreg
  555. else
  556. begin
  557. cg.a_op_reg_reg(current_asmdata.CurrAsmList,OP_ADD,OS_ADDR,sref.ref.base,offsetreg);
  558. sref.ref.base := offsetreg;
  559. end;
  560. cg.a_op_const_reg(current_asmdata.CurrAsmList,OP_AND,OS_INT,(1 shl (3+alignpower))-1,hreg);
  561. sref.bitindexreg := hreg;
  562. sref.startbit := 0;
  563. sref.bitlen := resultdef.packedbitsize;
  564. if (left.location.loc = LOC_REFERENCE) then
  565. location.loc := LOC_SUBSETREF
  566. else
  567. location.loc := LOC_CSUBSETREF;
  568. location.sref := sref;
  569. end;
  570. procedure tcgvecnode.second_wideansistring;
  571. begin
  572. end;
  573. procedure tcgvecnode.second_dynamicarray;
  574. begin
  575. end;
  576. procedure tcgvecnode.rangecheck_array;
  577. var
  578. hightree : tnode;
  579. poslabel,
  580. neglabel : tasmlabel;
  581. hreg : tregister;
  582. paraloc1,paraloc2 : tcgpara;
  583. begin
  584. paraloc1.init;
  585. paraloc2.init;
  586. if is_open_array(left.resultdef) or
  587. is_array_of_const(left.resultdef) then
  588. begin
  589. { cdecl functions don't have high() so we can not check the range }
  590. { (can't use current_procdef, since it may be a nested procedure) }
  591. if not(tprocdef(tparasymtable(tparavarsym(tloadnode(left).symtableentry).owner).defowner).proccalloption in [pocall_cdecl,pocall_cppdecl]) then
  592. begin
  593. { Get high value }
  594. hightree:=load_high_value_node(tparavarsym(tloadnode(left).symtableentry));
  595. { it must be available }
  596. if not assigned(hightree) then
  597. internalerror(200212201);
  598. firstpass(hightree);
  599. secondpass(hightree);
  600. { generate compares }
  601. if (right.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
  602. hreg:=cg.makeregsize(current_asmdata.CurrAsmList,right.location.register,OS_INT)
  603. else
  604. begin
  605. hreg:=cg.getintregister(current_asmdata.CurrAsmList,OS_INT);
  606. cg.a_load_loc_reg(current_asmdata.CurrAsmList,OS_INT,right.location,hreg);
  607. end;
  608. current_asmdata.getjumplabel(neglabel);
  609. current_asmdata.getjumplabel(poslabel);
  610. cg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_LT,0,hreg,poslabel);
  611. cg.a_cmp_loc_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_BE,hightree.location,hreg,neglabel);
  612. cg.a_label(current_asmdata.CurrAsmList,poslabel);
  613. cg.a_call_name(current_asmdata.CurrAsmList,'FPC_RANGEERROR',false);
  614. cg.a_label(current_asmdata.CurrAsmList,neglabel);
  615. { release hightree }
  616. hightree.free;
  617. end;
  618. end
  619. else
  620. if is_dynamic_array(left.resultdef) then
  621. begin
  622. paramanager.getintparaloc(pocall_default,1,paraloc1);
  623. paramanager.getintparaloc(pocall_default,2,paraloc2);
  624. paramanager.allocparaloc(current_asmdata.CurrAsmList,paraloc2);
  625. cg.a_param_loc(current_asmdata.CurrAsmList,right.location,paraloc2);
  626. paramanager.allocparaloc(current_asmdata.CurrAsmList,paraloc1);
  627. cg.a_param_loc(current_asmdata.CurrAsmList,left.location,paraloc1);
  628. paramanager.freeparaloc(current_asmdata.CurrAsmList,paraloc1);
  629. paramanager.freeparaloc(current_asmdata.CurrAsmList,paraloc2);
  630. cg.allocallcpuregisters(current_asmdata.CurrAsmList);
  631. cg.a_call_name(current_asmdata.CurrAsmList,'FPC_DYNARRAY_RANGECHECK',false);
  632. cg.deallocallcpuregisters(current_asmdata.CurrAsmList);
  633. end
  634. else
  635. cg.g_rangecheck(current_asmdata.CurrAsmList,right.location,right.resultdef,left.resultdef);
  636. paraloc1.done;
  637. paraloc2.done;
  638. end;
  639. procedure tcgvecnode.pass_generate_code;
  640. var
  641. offsetdec,
  642. extraoffset : aint;
  643. t : tnode;
  644. href : treference;
  645. otl,ofl : tasmlabel;
  646. newsize : tcgsize;
  647. mulsize,
  648. bytemulsize,
  649. alignpow : aint;
  650. isjump : boolean;
  651. paraloc1,
  652. paraloc2 : tcgpara;
  653. subsetref : tsubsetreference;
  654. temp : longint;
  655. begin
  656. paraloc1.init;
  657. paraloc2.init;
  658. mulsize:=get_mul_size;
  659. if not is_packed_array(left.resultdef) then
  660. bytemulsize:=mulsize
  661. else
  662. bytemulsize:=mulsize div 8;
  663. newsize:=def_cgsize(resultdef);
  664. secondpass(left);
  665. if left.location.loc=LOC_CREFERENCE then
  666. location_reset_ref(location,LOC_CREFERENCE,newsize,left.location.reference.alignment)
  667. else
  668. location_reset_ref(location,LOC_REFERENCE,newsize,left.location.reference.alignment);
  669. { an ansistring needs to be dereferenced }
  670. if is_ansistring(left.resultdef) or
  671. is_widestring(left.resultdef) or
  672. is_unicodestring(left.resultdef) then
  673. begin
  674. if nf_callunique in flags then
  675. internalerror(200304236);
  676. {DM!!!!!}
  677. case left.location.loc of
  678. LOC_REGISTER,
  679. LOC_CREGISTER :
  680. begin
  681. {$ifdef m68k}
  682. location.reference.base:=cg.getaddressregister(current_asmdata.CurrAsmList);
  683. cg.a_load_reg_reg(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,left.location.register,location.reference.base);
  684. {$else m68k}
  685. location.reference.base:=left.location.register;
  686. {$endif m68k}
  687. end;
  688. LOC_CREFERENCE,
  689. LOC_REFERENCE :
  690. begin
  691. location.reference.base:=cg.getaddressregister(current_asmdata.CurrAsmList);
  692. cg.a_load_ref_reg(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,left.location.reference,location.reference.base);
  693. end;
  694. else
  695. internalerror(2002032218);
  696. end;
  697. { check for a zero length string,
  698. we can use the ansistring routine here }
  699. if (cs_check_range in current_settings.localswitches) then
  700. begin
  701. paramanager.getintparaloc(pocall_default,1,paraloc1);
  702. paramanager.allocparaloc(current_asmdata.CurrAsmList,paraloc1);
  703. cg.a_param_reg(current_asmdata.CurrAsmList,OS_ADDR,location.reference.base,paraloc1);
  704. paramanager.freeparaloc(current_asmdata.CurrAsmList,paraloc1);
  705. cg.allocallcpuregisters(current_asmdata.CurrAsmList);
  706. cg.a_call_name(current_asmdata.CurrAsmList,'FPC_'+upper(tstringdef(left.resultdef).stringtypname)+'_CHECKZERO',false);
  707. cg.deallocallcpuregisters(current_asmdata.CurrAsmList);
  708. end;
  709. { in ansistrings/widestrings S[1] is p<w>char(S)[0] !! }
  710. if is_ansistring(left.resultdef) then
  711. offsetdec:=1
  712. else
  713. offsetdec:=2;
  714. location.reference.alignment:=offsetdec;
  715. dec(location.reference.offset,offsetdec);
  716. end
  717. else if is_dynamic_array(left.resultdef) then
  718. begin
  719. case left.location.loc of
  720. LOC_REGISTER,
  721. LOC_CREGISTER :
  722. location.reference.base:=left.location.register;
  723. LOC_REFERENCE,
  724. LOC_CREFERENCE :
  725. begin
  726. location.reference.base:=cg.getaddressregister(current_asmdata.CurrAsmList);
  727. cg.a_load_ref_reg(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,
  728. left.location.reference,location.reference.base);
  729. end;
  730. else
  731. internalerror(2002032219);
  732. end;
  733. { a dynarray points to the start of a memory block, which
  734. we assume to be always aligned to a multiple of the
  735. pointer size
  736. }
  737. location.reference.alignment:=sizeof(pint);
  738. end
  739. else
  740. location_copy(location,left.location);
  741. { location must be memory }
  742. if not(location.loc in [LOC_REFERENCE,LOC_CREFERENCE]) then
  743. internalerror(200411013);
  744. { offset can only differ from 0 if arraydef }
  745. if (left.resultdef.typ=arraydef) and
  746. not(is_dynamic_array(left.resultdef)) and
  747. (not(is_packed_array(left.resultdef)) or
  748. ((mulsize mod 8 = 0) and
  749. ispowerof2(mulsize div 8,temp)) or
  750. { only orddefs are bitpacked }
  751. not is_ordinal(resultdef)) then
  752. dec(location.reference.offset,bytemulsize*tarraydef(left.resultdef).lowrange);
  753. if right.nodetype=ordconstn then
  754. begin
  755. { offset can only differ from 0 if arraydef }
  756. case left.resultdef.typ of
  757. arraydef :
  758. begin
  759. { do not do any range checking when this is an array access to a pointer which has been
  760. typecasted from an array }
  761. if (not (ado_isconvertedpointer in tarraydef(left.resultdef).arrayoptions)) then
  762. begin
  763. if not(is_open_array(left.resultdef)) and
  764. not(is_array_of_const(left.resultdef)) and
  765. not(is_dynamic_array(left.resultdef)) then
  766. begin
  767. if (tordconstnode(right).value.svalue>tarraydef(left.resultdef).highrange) or
  768. (tordconstnode(right).value.svalue<tarraydef(left.resultdef).lowrange) then
  769. begin
  770. { this should be caught in the typecheckpass! (JM) }
  771. if (cs_check_range in current_settings.localswitches) then
  772. CGMessage(parser_e_range_check_error)
  773. else
  774. CGMessage(parser_w_range_check_error);
  775. end;
  776. end
  777. else
  778. begin
  779. { range checking for open and dynamic arrays needs
  780. runtime code }
  781. secondpass(right);
  782. if (cs_check_range in current_settings.localswitches) then
  783. rangecheck_array;
  784. end;
  785. end;
  786. end;
  787. stringdef :
  788. begin
  789. if (cs_check_range in current_settings.localswitches) then
  790. begin
  791. case tstringdef(left.resultdef).stringtype of
  792. { it's the same for ansi- and wide strings }
  793. st_unicodestring,
  794. st_widestring,
  795. st_ansistring:
  796. begin
  797. paramanager.getintparaloc(pocall_default,1,paraloc1);
  798. paramanager.getintparaloc(pocall_default,2,paraloc2);
  799. paramanager.allocparaloc(current_asmdata.CurrAsmList,paraloc2);
  800. cg.a_param_const(current_asmdata.CurrAsmList,OS_INT,tordconstnode(right).value.svalue,paraloc2);
  801. href:=location.reference;
  802. paramanager.allocparaloc(current_asmdata.CurrAsmList,paraloc1);
  803. if not(tf_winlikewidestring in target_info.flags) or
  804. (tstringdef(left.resultdef).stringtype<>st_widestring) then
  805. begin
  806. dec(href.offset,sizeof(pint)-offsetdec);
  807. cg.a_param_ref(current_asmdata.CurrAsmList,OS_ADDR,href,paraloc1);
  808. end
  809. else
  810. begin
  811. { winlike widestrings have a 4 byte length }
  812. dec(href.offset,4-offsetdec);
  813. cg.a_param_ref(current_asmdata.CurrAsmList,OS_32,href,paraloc1);
  814. end;
  815. paramanager.freeparaloc(current_asmdata.CurrAsmList,paraloc1);
  816. paramanager.freeparaloc(current_asmdata.CurrAsmList,paraloc2);
  817. cg.allocallcpuregisters(current_asmdata.CurrAsmList);
  818. cg.a_call_name(current_asmdata.CurrAsmList,'FPC_'+upper(tstringdef(left.resultdef).stringtypname)+'_RANGECHECK',false);
  819. cg.deallocallcpuregisters(current_asmdata.CurrAsmList);
  820. end;
  821. st_shortstring:
  822. begin
  823. {!!!!!!!!!!!!!!!!!}
  824. { if this one is implemented making use of the high parameter for openshortstrings, update ncgutils.do_get_used_regvars() too (JM) }
  825. end;
  826. st_longstring:
  827. begin
  828. {!!!!!!!!!!!!!!!!!}
  829. end;
  830. end;
  831. end;
  832. end;
  833. end;
  834. if not(is_packed_array(left.resultdef)) or
  835. ((mulsize mod 8 = 0) and
  836. (ispowerof2(mulsize div 8,temp) or
  837. { only orddefs are bitpacked }
  838. not is_ordinal(resultdef))) then
  839. begin
  840. extraoffset:=bytemulsize*tordconstnode(right).value.svalue;
  841. inc(location.reference.offset,extraoffset);
  842. { adjust alignment after to this change }
  843. location.reference.alignment:=newalignment(location.reference.alignment,extraoffset);
  844. { don't do this for floats etc.; needed to properly set the }
  845. { size for bitpacked arrays (e.g. a bitpacked array of }
  846. { enums who are size 2 but fit in one byte -> in the array }
  847. { they will be one byte and have to be stored like that) }
  848. if is_packed_array(left.resultdef) and
  849. (tcgsize2size[newsize] <> bytemulsize) then
  850. newsize:=int_cgsize(bytemulsize);
  851. end
  852. else
  853. begin
  854. subsetref.ref := location.reference;
  855. subsetref.ref.alignment := left.resultdef.alignment;
  856. if not ispowerof2(subsetref.ref.alignment,temp) then
  857. internalerror(2006081212);
  858. alignpow:=temp;
  859. inc(subsetref.ref.offset,((mulsize * (tordconstnode(right).value.svalue-tarraydef(left.resultdef).lowrange)) shr (3+alignpow)) shl alignpow);
  860. subsetref.bitindexreg := NR_NO;
  861. subsetref.startbit := (mulsize * (tordconstnode(right).value.svalue-tarraydef(left.resultdef).lowrange)) and ((1 shl (3+alignpow))-1);
  862. subsetref.bitlen := resultdef.packedbitsize;
  863. if (left.location.loc = LOC_REFERENCE) then
  864. location.loc := LOC_SUBSETREF
  865. else
  866. location.loc := LOC_CSUBSETREF;
  867. location.sref := subsetref;
  868. end;
  869. end
  870. else
  871. { not nodetype=ordconstn }
  872. begin
  873. if (cs_opt_level1 in current_settings.optimizerswitches) and
  874. { if we do range checking, we don't }
  875. { need that fancy code (it would be }
  876. { buggy) }
  877. not(cs_check_range in current_settings.localswitches) and
  878. (left.resultdef.typ=arraydef) and
  879. not is_packed_array(left.resultdef) then
  880. begin
  881. extraoffset:=0;
  882. if (right.nodetype=addn) then
  883. begin
  884. if taddnode(right).right.nodetype=ordconstn then
  885. begin
  886. extraoffset:=tordconstnode(taddnode(right).right).value.svalue;
  887. t:=taddnode(right).left;
  888. taddnode(right).left:=nil;
  889. right.free;
  890. right:=t;
  891. end
  892. else if taddnode(right).left.nodetype=ordconstn then
  893. begin
  894. extraoffset:=tordconstnode(taddnode(right).left).value.svalue;
  895. t:=taddnode(right).right;
  896. taddnode(right).right:=nil;
  897. right.free;
  898. right:=t;
  899. end;
  900. end
  901. else if (right.nodetype=subn) then
  902. begin
  903. if taddnode(right).right.nodetype=ordconstn then
  904. begin
  905. extraoffset:=-tordconstnode(taddnode(right).right).value.svalue;
  906. t:=taddnode(right).left;
  907. taddnode(right).left:=nil;
  908. right.free;
  909. right:=t;
  910. end;
  911. end;
  912. inc(location.reference.offset,
  913. mulsize*extraoffset);
  914. end;
  915. { calculate from left to right }
  916. if not(location.loc in [LOC_CREFERENCE,LOC_REFERENCE]) then
  917. internalerror(200304237);
  918. isjump:=(right.expectloc=LOC_JUMP);
  919. if isjump then
  920. begin
  921. otl:=current_procinfo.CurrTrueLabel;
  922. current_asmdata.getjumplabel(current_procinfo.CurrTrueLabel);
  923. ofl:=current_procinfo.CurrFalseLabel;
  924. current_asmdata.getjumplabel(current_procinfo.CurrFalseLabel);
  925. end;
  926. secondpass(right);
  927. { if mulsize = 1, we won't have to modify the index }
  928. location_force_reg(current_asmdata.CurrAsmList,right.location,OS_ADDR,true);
  929. if isjump then
  930. begin
  931. current_procinfo.CurrTrueLabel:=otl;
  932. current_procinfo.CurrFalseLabel:=ofl;
  933. end
  934. else if (right.location.loc = LOC_JUMP) then
  935. internalerror(2006010801);
  936. { only range check now, we can't range check loc_flags/loc_jump }
  937. if cs_check_range in current_settings.localswitches then
  938. begin
  939. if left.resultdef.typ=arraydef then
  940. rangecheck_array;
  941. end;
  942. { produce possible range check code: }
  943. if cs_check_range in current_settings.localswitches then
  944. begin
  945. if left.resultdef.typ=arraydef then
  946. begin
  947. { done defore (PM) }
  948. end
  949. else if (left.resultdef.typ=stringdef) then
  950. begin
  951. case tstringdef(left.resultdef).stringtype of
  952. { it's the same for ansi- and wide strings }
  953. st_unicodestring,
  954. st_widestring,
  955. st_ansistring:
  956. begin
  957. paramanager.getintparaloc(pocall_default,1,paraloc1);
  958. paramanager.getintparaloc(pocall_default,2,paraloc2);
  959. paramanager.allocparaloc(current_asmdata.CurrAsmList,paraloc2);
  960. cg.a_param_reg(current_asmdata.CurrAsmList,OS_INT,right.location.register,paraloc2);
  961. href:=location.reference;
  962. dec(href.offset,sizeof(pint)-offsetdec);
  963. href:=location.reference;
  964. paramanager.allocparaloc(current_asmdata.CurrAsmList,paraloc1);
  965. if not(tf_winlikewidestring in target_info.flags) or
  966. (tstringdef(left.resultdef).stringtype<>st_widestring) then
  967. begin
  968. dec(href.offset,sizeof(pint)-offsetdec);
  969. cg.a_param_ref(current_asmdata.CurrAsmList,OS_ADDR,href,paraloc1);
  970. end
  971. else
  972. begin
  973. { winlike widestrings have a 4 byte length }
  974. dec(href.offset,4-offsetdec);
  975. cg.a_param_ref(current_asmdata.CurrAsmList,OS_32,href,paraloc1);
  976. end;
  977. paramanager.freeparaloc(current_asmdata.CurrAsmList,paraloc1);
  978. paramanager.freeparaloc(current_asmdata.CurrAsmList,paraloc2);
  979. cg.allocallcpuregisters(current_asmdata.CurrAsmList);
  980. cg.a_call_name(current_asmdata.CurrAsmList,'FPC_'+upper(tstringdef(left.resultdef).stringtypname)+'_RANGECHECK',false);
  981. cg.deallocallcpuregisters(current_asmdata.CurrAsmList);
  982. end;
  983. st_shortstring:
  984. begin
  985. {!!!!!!!!!!!!!!!!!}
  986. end;
  987. st_longstring:
  988. begin
  989. {!!!!!!!!!!!!!!!!!}
  990. end;
  991. end;
  992. end;
  993. end;
  994. { insert the register and the multiplication factor in the
  995. reference }
  996. if not is_packed_array(left.resultdef) then
  997. update_reference_reg_mul(right.location.register,mulsize)
  998. else
  999. update_reference_reg_packed(right.location.register,mulsize);
  1000. end;
  1001. location.size:=newsize;
  1002. paraloc1.done;
  1003. paraloc2.done;
  1004. end;
  1005. begin
  1006. cloadvmtaddrnode:=tcgloadvmtaddrnode;
  1007. cloadparentfpnode:=tcgloadparentfpnode;
  1008. caddrnode:=tcgaddrnode;
  1009. cderefnode:=tcgderefnode;
  1010. csubscriptnode:=tcgsubscriptnode;
  1011. cwithnode:=tcgwithnode;
  1012. cvecnode:=tcgvecnode;
  1013. end.