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

a) cpu64bitaddr, which means that we are generating a compiler which will generate code for targets with a 64 bit address space/abi b) cpu64bitalu, which means that we are generating a compiler which will generate code for a cpu with support for 64 bit integer operations (possibly running in a 32 bit address space, depending on the cpu64bitaddr define) All cpus which had cpu64bit set now have both the above defines set, and none of the 32 bit cpus have cpu64bitalu set (and none will compile with it currently) + pint and puint types, similar to aint/aword (not pword because that that conflicts with pword=^word) * several changes from aint/aword to pint/pword * some changes of tcgsize2size[OS_INT] to sizeof(pint) git-svn-id: trunk@10320 -
587 lines
18 KiB
ObjectPascal
587 lines
18 KiB
ObjectPascal
{
|
|
Copyright (c) 1999-2002 by Jonas Maebe and Thomas Schatzl
|
|
|
|
Contains the assembler object for the PowerPC 32 and PowerPC 64
|
|
|
|
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 aasmcpu;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
globtype,verbose,
|
|
aasmbase,aasmtai,aasmdata,aasmsym,
|
|
cpubase,cgbase,cgutils;
|
|
|
|
const
|
|
{ "mov reg,reg" source operand number }
|
|
O_MOV_SOURCE = 1;
|
|
{ "mov reg,reg" source operand number }
|
|
O_MOV_DEST = 0;
|
|
|
|
|
|
type
|
|
taicpu = class(tai_cpu_abstract_sym)
|
|
constructor op_none(op : tasmop);
|
|
|
|
constructor op_reg(op : tasmop;_op1 : tregister);
|
|
constructor op_const(op : tasmop;_op1 : aint);
|
|
|
|
constructor op_reg_reg(op : tasmop;_op1,_op2 : tregister);
|
|
constructor op_reg_ref(op : tasmop;_op1 : tregister;const _op2 : treference);
|
|
constructor op_reg_const(op:tasmop; _op1: tregister; _op2: aint);
|
|
constructor op_const_reg(op:tasmop; _op1: aint; _op2: tregister);
|
|
|
|
constructor op_const_const(op : tasmop;_op1,_op2 : aint);
|
|
|
|
constructor op_reg_reg_const_const(op: tasmop; _op1, _op2: tregister; _op3, _op4: aint);
|
|
|
|
constructor op_reg_reg_reg(op : tasmop;_op1,_op2,_op3 : tregister);
|
|
constructor op_reg_reg_const(op : tasmop;_op1,_op2 : tregister; _op3: aint);
|
|
constructor op_reg_reg_sym_ofs(op : tasmop;_op1,_op2 : tregister; _op3: tasmsymbol;_op3ofs: aint);
|
|
constructor op_reg_reg_ref(op : tasmop;_op1,_op2 : tregister; const _op3: treference);
|
|
constructor op_const_reg_reg(op : tasmop;_op1 : aint;_op2, _op3 : tregister);
|
|
constructor op_const_reg_const(op : tasmop;_op1 : aint;_op2 : tregister;_op3 : aint);
|
|
constructor op_const_const_const(op : tasmop;_op1 : aint;_op2 : aint;_op3 : aint);
|
|
|
|
constructor op_reg_reg_reg_reg(op : tasmop;_op1,_op2,_op3,_op4 : tregister);
|
|
constructor op_reg_bool_reg_reg(op : tasmop;_op1: tregister;_op2:boolean;_op3,_op4:tregister);
|
|
constructor op_reg_bool_reg_const(op : tasmop;_op1: tregister;_op2:boolean;_op3:tregister;_op4: aint);
|
|
|
|
constructor op_reg_reg_reg_const_const(op : tasmop;_op1,_op2,_op3 : tregister;_op4,_op5 : aint);
|
|
constructor op_reg_reg_const_const_const(op : tasmop;_op1,_op2 : tregister;_op3,_op4,_op5 : aint);
|
|
|
|
|
|
{ this is for Jmp instructions }
|
|
constructor op_cond_sym(op : tasmop;cond:TAsmCond;_op1 : tasmsymbol);
|
|
constructor op_const_const_sym(op : tasmop;_op1,_op2 : aint;_op3: tasmsymbol);
|
|
|
|
|
|
constructor op_sym(op : tasmop;_op1 : tasmsymbol);
|
|
constructor op_sym_ofs(op : tasmop;_op1 : tasmsymbol;_op1ofs:aint);
|
|
constructor op_reg_sym_ofs(op : tasmop;_op1 : tregister;_op2:tasmsymbol;_op2ofs : aint);
|
|
constructor op_sym_ofs_ref(op : tasmop;_op1 : tasmsymbol;_op1ofs:aint;const _op2 : treference);
|
|
|
|
procedure loadbool(opidx:aint;_b:boolean);
|
|
|
|
function is_same_reg_move(regtype: Tregistertype):boolean; override;
|
|
|
|
{ register spilling code }
|
|
function spilling_get_operation_type(opnr: longint): topertype;override;
|
|
function spilling_get_operation_type_ref(opnr: longint; reg: tregister): topertype;override;
|
|
end;
|
|
|
|
tai_align = class(tai_align_abstract)
|
|
{ nothing to add }
|
|
end;
|
|
|
|
procedure InitAsm;
|
|
procedure DoneAsm;
|
|
|
|
|
|
function spilling_create_load(const ref:treference;r:tregister):Taicpu;
|
|
function spilling_create_store(r:tregister; const ref:treference):Taicpu;
|
|
|
|
procedure fixup_jmps(list: TAsmList);
|
|
|
|
implementation
|
|
|
|
uses cutils, cclasses;
|
|
|
|
{*****************************************************************************
|
|
taicpu Constructors
|
|
*****************************************************************************}
|
|
|
|
procedure taicpu.loadbool(opidx:aint;_b:boolean);
|
|
begin
|
|
if opidx>=ops then
|
|
ops:=opidx+1;
|
|
with oper[opidx]^ do
|
|
begin
|
|
if typ=top_ref then
|
|
dispose(ref);
|
|
b:=_b;
|
|
typ:=top_bool;
|
|
end;
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_none(op : tasmop);
|
|
begin
|
|
inherited create(op);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_reg(op : tasmop;_op1 : tregister);
|
|
begin
|
|
inherited create(op);
|
|
ops:=1;
|
|
loadreg(0,_op1);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_const(op : tasmop;_op1 : aint);
|
|
begin
|
|
inherited create(op);
|
|
ops:=1;
|
|
loadconst(0,_op1);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_reg_reg(op : tasmop;_op1,_op2 : tregister);
|
|
begin
|
|
inherited create(op);
|
|
ops:=2;
|
|
loadreg(0,_op1);
|
|
loadreg(1,_op2);
|
|
end;
|
|
|
|
constructor taicpu.op_reg_const(op:tasmop; _op1: tregister; _op2: aint);
|
|
begin
|
|
inherited create(op);
|
|
ops:=2;
|
|
loadreg(0,_op1);
|
|
loadconst(1,_op2);
|
|
end;
|
|
|
|
constructor taicpu.op_const_reg(op:tasmop; _op1: aint; _op2: tregister);
|
|
begin
|
|
inherited create(op);
|
|
ops:=2;
|
|
loadconst(0,_op1);
|
|
loadreg(1,_op2);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_reg_ref(op : tasmop;_op1 : tregister;const _op2 : treference);
|
|
begin
|
|
inherited create(op);
|
|
ops:=2;
|
|
loadreg(0,_op1);
|
|
loadref(1,_op2);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_const_const(op : tasmop;_op1,_op2 : aint);
|
|
begin
|
|
inherited create(op);
|
|
ops:=2;
|
|
loadconst(0,_op1);
|
|
loadconst(1,_op2);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_reg_reg_const_const(op: tasmop; _op1, _op2: tregister; _op3, _op4: aint);
|
|
begin
|
|
inherited create(op);
|
|
ops := 4;
|
|
loadreg(0, _op1);
|
|
loadreg(1, _op2);
|
|
loadconst(2, _op3);
|
|
loadconst(3, _op4);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_reg_reg_reg(op : tasmop;_op1,_op2,_op3 : tregister);
|
|
begin
|
|
inherited create(op);
|
|
ops:=3;
|
|
loadreg(0,_op1);
|
|
loadreg(1,_op2);
|
|
loadreg(2,_op3);
|
|
end;
|
|
|
|
constructor taicpu.op_reg_reg_const(op : tasmop;_op1,_op2 : tregister; _op3: aint);
|
|
begin
|
|
inherited create(op);
|
|
ops:=3;
|
|
loadreg(0,_op1);
|
|
loadreg(1,_op2);
|
|
loadconst(2,_op3);
|
|
end;
|
|
|
|
constructor taicpu.op_reg_reg_sym_ofs(op : tasmop;_op1,_op2 : tregister; _op3: tasmsymbol;_op3ofs: aint);
|
|
begin
|
|
inherited create(op);
|
|
ops:=3;
|
|
loadreg(0,_op1);
|
|
loadreg(1,_op2);
|
|
loadsymbol(0,_op3,_op3ofs);
|
|
end;
|
|
|
|
constructor taicpu.op_reg_reg_ref(op : tasmop;_op1,_op2 : tregister; const _op3: treference);
|
|
begin
|
|
inherited create(op);
|
|
ops:=3;
|
|
loadreg(0,_op1);
|
|
loadreg(1,_op2);
|
|
loadref(2,_op3);
|
|
end;
|
|
|
|
constructor taicpu.op_const_reg_reg(op : tasmop;_op1 : aint;_op2, _op3 : tregister);
|
|
begin
|
|
inherited create(op);
|
|
ops:=3;
|
|
loadconst(0,_op1);
|
|
loadreg(1,_op2);
|
|
loadreg(2,_op3);
|
|
end;
|
|
|
|
constructor taicpu.op_const_reg_const(op : tasmop;_op1 : aint;_op2 : tregister;_op3 : aint);
|
|
begin
|
|
inherited create(op);
|
|
ops:=3;
|
|
loadconst(0,_op1);
|
|
loadreg(1,_op2);
|
|
loadconst(2,_op3);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_const_const_const(op : tasmop;_op1 : aint;_op2 : aint;_op3 : aint);
|
|
begin
|
|
inherited create(op);
|
|
ops:=3;
|
|
loadconst(0,_op1);
|
|
loadconst(1,_op2);
|
|
loadconst(2,_op3);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_reg_reg_reg_reg(op : tasmop;_op1,_op2,_op3,_op4 : tregister);
|
|
begin
|
|
inherited create(op);
|
|
ops:=4;
|
|
loadreg(0,_op1);
|
|
loadreg(1,_op2);
|
|
loadreg(2,_op3);
|
|
loadreg(3,_op4);
|
|
end;
|
|
|
|
constructor taicpu.op_reg_bool_reg_reg(op : tasmop;_op1: tregister;_op2:boolean;_op3,_op4:tregister);
|
|
begin
|
|
inherited create(op);
|
|
ops:=4;
|
|
loadreg(0,_op1);
|
|
loadbool(1,_op2);
|
|
loadreg(2,_op3);
|
|
loadreg(3,_op4);
|
|
end;
|
|
|
|
constructor taicpu.op_reg_bool_reg_const(op : tasmop;_op1: tregister;_op2:boolean;_op3:tregister;_op4: aint);
|
|
begin
|
|
inherited create(op);
|
|
ops:=4;
|
|
loadreg(0,_op1);
|
|
loadbool(0,_op2);
|
|
loadreg(0,_op3);
|
|
loadconst(0,cardinal(_op4));
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_reg_reg_reg_const_const(op : tasmop;_op1,_op2,_op3 : tregister;_op4,_op5 : aint);
|
|
begin
|
|
inherited create(op);
|
|
ops:=5;
|
|
loadreg(0,_op1);
|
|
loadreg(1,_op2);
|
|
loadreg(2,_op3);
|
|
loadconst(3,cardinal(_op4));
|
|
loadconst(4,cardinal(_op5));
|
|
end;
|
|
|
|
constructor taicpu.op_reg_reg_const_const_const(op : tasmop;_op1,_op2 : tregister;_op3,_op4,_op5 : aint);
|
|
begin
|
|
inherited create(op);
|
|
ops:=5;
|
|
loadreg(0,_op1);
|
|
loadreg(1,_op2);
|
|
loadconst(2,_op3);
|
|
loadconst(3,_op4);
|
|
loadconst(4,_op5);
|
|
end;
|
|
|
|
constructor taicpu.op_cond_sym(op : tasmop;cond:TAsmCond;_op1 : tasmsymbol);
|
|
begin
|
|
inherited create(op);
|
|
condition:=cond;
|
|
ops:=1;
|
|
loadsymbol(0,_op1,0);
|
|
end;
|
|
|
|
constructor taicpu.op_const_const_sym(op : tasmop;_op1,_op2 : aint; _op3: tasmsymbol);
|
|
begin
|
|
inherited create(op);
|
|
ops:=3;
|
|
loadconst(0,_op1);
|
|
loadconst(1,_op2);
|
|
loadsymbol(2,_op3,0);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_sym(op : tasmop;_op1 : tasmsymbol);
|
|
begin
|
|
inherited create(op);
|
|
ops:=1;
|
|
loadsymbol(0,_op1,0);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_sym_ofs(op : tasmop;_op1 : tasmsymbol;_op1ofs:aint);
|
|
begin
|
|
inherited create(op);
|
|
ops:=1;
|
|
loadsymbol(0,_op1,_op1ofs);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_reg_sym_ofs(op : tasmop;_op1 : tregister;_op2:tasmsymbol;_op2ofs : aint);
|
|
begin
|
|
inherited create(op);
|
|
ops:=2;
|
|
loadreg(0,_op1);
|
|
loadsymbol(1,_op2,_op2ofs);
|
|
end;
|
|
|
|
|
|
constructor taicpu.op_sym_ofs_ref(op : tasmop;_op1 : tasmsymbol;_op1ofs:aint;const _op2 : treference);
|
|
begin
|
|
inherited create(op);
|
|
ops:=2;
|
|
loadsymbol(0,_op1,_op1ofs);
|
|
loadref(1,_op2);
|
|
end;
|
|
|
|
|
|
{ ****************************** newra stuff *************************** }
|
|
|
|
function taicpu.is_same_reg_move(regtype: Tregistertype):boolean;
|
|
begin
|
|
result :=
|
|
(((opcode=A_MR) and
|
|
(regtype = R_INTREGISTER)) or
|
|
((opcode = A_FMR) and
|
|
(regtype = R_FPUREGISTER))) and
|
|
{ these opcodes can only have registers as operands }
|
|
(oper[0]^.reg=oper[1]^.reg);
|
|
end;
|
|
|
|
|
|
function taicpu.spilling_get_operation_type(opnr: longint): topertype;
|
|
begin
|
|
result := operand_read;
|
|
case opcode of
|
|
A_STMW,A_LMW:
|
|
internalerror(2005021805);
|
|
A_STBU, A_STBUX, A_STHU, A_STHUX,
|
|
A_STWU, A_STWUX,
|
|
A_STFSU, A_STFSUX, A_STFDU, A_STFDUX,
|
|
A_STB, A_STBX, A_STH, A_STHX,
|
|
A_STW, A_STWX,
|
|
A_STFS, A_STFSX, A_STFD, A_STFDX, A_STFIWX, A_STHBRX, A_STWBRX, A_STWCX_,
|
|
A_CMP, A_CMPI, A_CMPL, A_CMPLI,
|
|
A_DCBA, A_DCBI, A_DCBST, A_DCBT, A_DCBTST, A_DCBZ, A_DCBF, A_ICBI,
|
|
A_ECOWX, A_FCMPO, A_FCMPU, A_MTMSR, A_TLBIE, A_TW, A_TWI,
|
|
A_CMPWI, A_CMPW, A_CMPLWI, A_CMPLW, A_MT, A_MTLR, A_MTCTR
|
|
{$ifdef cpu64bitalu}
|
|
, A_STDU, A_STDUX,
|
|
A_STD, A_STDX,
|
|
A_STDCX_,
|
|
A_CMPD, A_CMPDI, A_CMPLD, A_CMPLDI,
|
|
A_MFXER
|
|
{$endif cpu64bitalu}
|
|
: ;
|
|
A_RLWIMI, A_RLWIMI_
|
|
{$ifdef cpu64bitalu}
|
|
, A_INSRDI, A_INSRDI_, A_RLDIMI
|
|
{$endif not cpu64bitalu}
|
|
:
|
|
if opnr = 0 then
|
|
result := operand_readwrite;
|
|
else
|
|
if opnr = 0 then
|
|
result := operand_write;
|
|
end;
|
|
end;
|
|
|
|
|
|
function taicpu.spilling_get_operation_type_ref(opnr: longint; reg: tregister): topertype;
|
|
begin
|
|
result := operand_read;
|
|
case opcode of
|
|
A_STBU, A_STBUX, A_STHU, A_STHUX, A_STWU, A_STWUX,
|
|
{$ifdef cpu64bitalu}
|
|
A_STDU, A_STDUX,
|
|
{$endif cpu64bitalu}
|
|
A_STFSU, A_STFSUX, A_STFDU, A_STFDUX:
|
|
if (oper[opnr]^.ref^.base = reg) then
|
|
result := operand_readwrite;
|
|
end;
|
|
end;
|
|
|
|
|
|
function spilling_create_load(const ref:treference;r:tregister):Taicpu;
|
|
begin
|
|
case getregtype(r) of
|
|
R_INTREGISTER:
|
|
{$ifdef cpu64bitalu}
|
|
result:=taicpu.op_reg_ref(A_LD,r,ref);
|
|
{$else cpu64bitalu}
|
|
result:=taicpu.op_reg_ref(A_LWZ,r,ref);
|
|
{$endif cpu64bitalu}
|
|
R_FPUREGISTER:
|
|
result:=taicpu.op_reg_ref(A_LFD,r,ref);
|
|
else
|
|
internalerror(2005123101);
|
|
end;
|
|
end;
|
|
|
|
|
|
function spilling_create_store(r:tregister; const ref:treference):Taicpu;
|
|
begin
|
|
case getregtype(r) of
|
|
R_INTREGISTER:
|
|
{$ifdef cpu64bitalu}
|
|
result:=taicpu.op_reg_ref(A_STD,r,ref);
|
|
{$else cpu64bitalu}
|
|
result:=taicpu.op_reg_ref(A_STW,r,ref);
|
|
{$endif cpu64bitalu}
|
|
R_FPUREGISTER:
|
|
result:=taicpu.op_reg_ref(A_STFD,r,ref);
|
|
else
|
|
internalerror(2005123102);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure InitAsm;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure DoneAsm;
|
|
begin
|
|
end;
|
|
|
|
|
|
procedure fixup_jmps(list: TAsmList);
|
|
var
|
|
p: tai;
|
|
newjmp: taicpu;
|
|
labelpositions: TFPList;
|
|
instrpos: ptrint;
|
|
l: tasmlabel;
|
|
inserted_something: boolean;
|
|
begin
|
|
// if certainly not enough instructions to cause an overflow, don't bother
|
|
if (list.count <= (high(smallint) div 4)) then
|
|
exit;
|
|
labelpositions := TFPList.create;
|
|
p := tai(list.first);
|
|
instrpos := 1;
|
|
// record label positions
|
|
while assigned(p) do
|
|
begin
|
|
if p.typ = ait_label then
|
|
begin
|
|
if (tai_label(p).labsym.labelnr >= labelpositions.count) then
|
|
labelpositions.count := tai_label(p).labsym.labelnr * 2;
|
|
labelpositions[tai_label(p).labsym.labelnr] := pointer(instrpos);
|
|
end;
|
|
if p.typ = ait_instruction then
|
|
inc(instrpos);
|
|
p := tai(p.next);
|
|
end;
|
|
|
|
// check and fix distances
|
|
repeat
|
|
inserted_something := false;
|
|
p := tai(list.first);
|
|
instrpos := 1;
|
|
while assigned(p) do
|
|
begin
|
|
case p.typ of
|
|
ait_label:
|
|
// update labelposition in case it changed due to insertion
|
|
// of jumps
|
|
begin
|
|
// can happen because of newly inserted labels
|
|
if (tai_label(p).labsym.labelnr > labelpositions.count) then
|
|
labelpositions.count := tai_label(p).labsym.labelnr * 2;
|
|
labelpositions[tai_label(p).labsym.labelnr] := pointer(instrpos);
|
|
end;
|
|
ait_instruction:
|
|
begin
|
|
inc(instrpos);
|
|
case taicpu(p).opcode of
|
|
A_BC:
|
|
if (taicpu(p).oper[0]^.typ = top_ref) and
|
|
assigned(taicpu(p).oper[0]^.ref^.symbol) and
|
|
(taicpu(p).oper[0]^.ref^.symbol is tasmlabel) and
|
|
(labelpositions[tasmlabel(taicpu(p).oper[0]^.ref^.symbol).labelnr] <> NIL) and
|
|
{$ifopt q+}
|
|
{$q-}
|
|
{$define overflowon}
|
|
{$endif}
|
|
(ptruint(abs(ptrint(labelpositions[tasmlabel(taicpu(p).oper[0]^.ref^.symbol).labelnr]-instrpos)) - (low(smallint) div 4)) > ptruint((high(smallint) - low(smallint)) div 4)) then
|
|
{$ifdef overflowon}
|
|
{$q+}
|
|
{$undef overflowon}
|
|
{$endif}
|
|
begin
|
|
// add a new label after this jump
|
|
current_asmdata.getjumplabel(l);
|
|
{ new label -> may have to increase array size }
|
|
if (l.labelnr >= labelpositions.count) then
|
|
labelpositions.count := l.labelnr + 10;
|
|
{ newjmp will be inserted before the label, and it's inserted after }
|
|
{ the current jump -> instrpos+2 }
|
|
labelpositions[l.labelnr] := pointer(instrpos+2);
|
|
list.insertafter(tai_label.create(l),p);
|
|
// add a new unconditional jump between this jump and the label
|
|
newjmp := taicpu.op_sym(A_B,taicpu(p).oper[0]^.ref^.symbol);
|
|
newjmp.is_jmp := true;
|
|
newjmp.fileinfo := taicpu(p).fileinfo;
|
|
list.insertafter(newjmp,p);
|
|
inc(instrpos);
|
|
// change the conditional jump to point to the newly inserted label
|
|
tasmlabel(taicpu(p).oper[0]^.ref^.symbol).decrefs;
|
|
taicpu(p).oper[0]^.ref^.symbol := l;
|
|
l.increfs;
|
|
// and invert its condition code
|
|
taicpu(p).condition := inverse_cond(taicpu(p).condition);
|
|
// we inserted an instruction, so will have to check everything again
|
|
inserted_something := true;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
p := tai(p.next);
|
|
end;
|
|
until not inserted_something;
|
|
labelpositions.free;
|
|
end;
|
|
|
|
|
|
begin
|
|
cai_align:=tai_align;
|
|
cai_cpu:=taicpu;
|
|
end.
|
|
|