Browse Source

* synchronized with trunk

git-svn-id: branches/wasm@46710 -
nickysn 5 years ago
parent
commit
325c91d152

+ 17 - 0
compiler/psub.pas

@@ -1165,6 +1165,23 @@ implementation
               end;
           end;
 {$endif defined(x86) or defined(arm)}
+{$if defined(xtensa)}
+        { On xtensa, the stack frame size can be estimated to avoid using an extra frame pointer,
+          in case parameters are passed on the stack.
+
+          However, the draw back is, if the estimation fails, compilation will break later on
+          with an internal error, so this switch is not enabled by default yet. To overcome this,
+          multipass compilation of subroutines must be supported
+        }
+        if (target_info.abi=abi_xtensa_windowed) and (procdef.stack_tainting_parameter(calleeside)) then
+          begin
+            include(flags,pi_estimatestacksize);
+            set_first_temp_offset;
+            procdef.has_paraloc_info:=callnoside;
+            generate_parameter_info;
+            exit;
+          end;
+{$endif defined(xtensa)}
         { set the start offset to the start of the temp area in the stack }
         set_first_temp_offset;
       end;

+ 1 - 1
compiler/xtensa/cgcpu.pas

@@ -1243,7 +1243,7 @@ implementation
               list.concat(taicpu.op_reg_reg(A_NEG,regdst.reghi,regsrc.reghi));
               list.concat(taicpu.op_reg_reg_const(A_ADDI,tmpreg,regdst.reghi,-1));
               instr:=taicpu.op_reg_reg_reg(A_MOV,regdst.reghi,tmpreg,regdst.reglo);
-              instr.condition:=C_EQZ;
+              instr.condition:=C_NEZ;
               list.concat(instr);
             end;
           OP_NOT:

+ 27 - 54
compiler/xtensa/cpupara.pas

@@ -43,7 +43,7 @@ unit cpupara;
        private
          { the max. register depends on the used call instruction }
          maxintreg : TSuperRegister;
-         procedure init_values(side: tcallercallee; var curintreg: tsuperregister; var cur_stack_offset: aword);
+         procedure init_values(p: tabstractprocdef; side: tcallercallee; var curintreg: tsuperregister; var cur_stack_offset: aword);
          function create_paraloc_info_intern(p : tabstractprocdef; side : tcallercallee;
            paras : tparalist; var curintreg : tsuperregister;
            var cur_stack_offset : aword; varargsparas : boolean) : longint;
@@ -54,7 +54,8 @@ unit cpupara;
     uses
        cpuinfo,globals,
        verbose,systems,
-       defutil,symtable,
+       defutil,
+       symtable,symcpu,
        procinfo,cpupi;
 
 
@@ -95,17 +96,14 @@ unit cpupara;
             classrefdef:
               result:=LOC_REGISTER;
             procvardef:
-              if (p.size = sizeof(pint)) then
-                result:=LOC_REGISTER
-              else
-                result:=LOC_REFERENCE;
+              result:=LOC_REGISTER;
             recorddef:
-              if (p.size > 4) then
+              if p.size>24 then
                 result:=LOC_REFERENCE
               else
                 result:=LOC_REGISTER;
             objectdef:
-              if is_object(p) then
+              if is_object(p) and (p.size>24) then
                 result:=LOC_REFERENCE
               else
                 result:=LOC_REGISTER;
@@ -117,7 +115,7 @@ unit cpupara;
             filedef:
               result:=LOC_REGISTER;
             arraydef:
-              if is_dynamic_array(p) then
+              if is_dynamic_array(p) or (p.size<=24) then
                 getparaloc:=LOC_REGISTER
               else
                 result:=LOC_REFERENCE;
@@ -127,12 +125,12 @@ unit cpupara;
               else
                 result:=LOC_REFERENCE;
             variantdef:
-              result:=LOC_REFERENCE;
+              result:=LOC_REGISTER;
             { avoid problems with errornous definitions }
             errordef:
               result:=LOC_REGISTER;
             else
-              internalerror(2002071001);
+              internalerror(2020082501);
          end;
       end;
 
@@ -150,33 +148,17 @@ unit cpupara;
           variantdef,
           formaldef :
             result:=true;
-          { regular procvars must be passed by value, because you cannot pass
-            the address of a local stack location when calling e.g.
-            pthread_create with the address of a function (first of all it
-            expects the address of the function to execute and not the address
-            of a memory location containing that address, and secondly if you
-            first store the address on the stack and then pass the address of
-            this stack location, then this stack location may no longer be
-            valid when the newly started thread accesses it.
-
-            However, for "procedure of object" we must use the same calling
-            convention as for "8 byte record" due to the need for
-            interchangeability with the TMethod record type.
-          }
-          procvardef :
-            result:=
-              (def.size <> sizeof(pint));
           recorddef :
-            result := (def.size > 8) or (varspez = vs_const);
+            result:=(varspez = vs_const);
           arraydef:
             result:=(tarraydef(def).highrange>=tarraydef(def).lowrange) or
                              is_open_array(def) or
                              is_array_of_const(def) or
                              is_array_constructor(def);
           objectdef :
-            result:=is_object(def);
+            result:=is_object(def) and (varspez = vs_const);
           setdef :
-            result:=not is_smallset(def);
+            result:=(varspez = vs_const);
           stringdef :
             result:=tstringdef(def).stringtype in [st_shortstring,st_longstring];
           else
@@ -185,7 +167,7 @@ unit cpupara;
       end;
 
 
-    procedure tcpuparamanager.init_values(side : tcallercallee; var curintreg: tsuperregister; var cur_stack_offset: aword);
+    procedure tcpuparamanager.init_values(p : tabstractprocdef; side : tcallercallee; var curintreg: tsuperregister; var cur_stack_offset: aword);
       begin
         cur_stack_offset:=0;
         case target_info.abi of
@@ -195,6 +177,8 @@ unit cpupara;
                 begin
                   curintreg:=RS_A2;
                   maxintreg:=RS_A7;
+                  if current_procinfo.framepointer=NR_STACK_POINTER_REG then
+                    cur_stack_offset:=(p as tcpuprocdef).total_stackframe_size;
                 end
               else
                 begin
@@ -287,7 +271,7 @@ unit cpupara;
         cur_stack_offset: aword;
         curintreg: tsuperregister;
       begin
-        init_values(side,curintreg,cur_stack_offset);
+        init_values(p,side,curintreg,cur_stack_offset);
 
         result := create_paraloc_info_intern(p,side,p.paras,curintreg,cur_stack_offset,false);
 
@@ -363,13 +347,19 @@ unit cpupara;
                     end;
                 end;
 
-              loc := getparaloc(paradef);
+              loc:=getparaloc(paradef);
+
+              if (loc=LOC_REGISTER) and ((maxintreg-nextintreg+1)*4<paradef.size) then
+                begin
+                  loc:=LOC_REFERENCE;
+                  nextintreg:=maxintreg+1;
+                end;
 
               hp.paraloc[side].alignment:=std_param_align;
               hp.paraloc[side].size:=paracgsize;
               hp.paraloc[side].intsize:=paralen;
               hp.paraloc[side].def:=paradef;
-              if (is_64bit(paradef)) and
+              if (loc=LOC_REGISTER) and (is_64bit(paradef)) and
                  odd(nextintreg-RS_A2) then
                 inc(nextintreg);
               if (paralen = 0) then
@@ -410,16 +400,6 @@ unit cpupara;
                           paraloc^.size:=paracgsize;
                           paraloc^.def:=locdef;
                         end;
-                      { aix requires that record data stored in parameter
-                        registers is left-aligned }
-                      if (target_info.system in systems_aix) and
-                         (paradef.typ = recorddef) and
-                         (paralen < sizeof(aint)) then
-                        begin
-                          paraloc^.shiftval := (sizeof(aint)-paralen)*(-8);
-                          paraloc^.size := OS_INT;
-                          paraloc^.def := u32inttype;
-                        end;
                       paraloc^.register:=newreg(R_INTREGISTER,nextintreg,R_SUBNONE);
                       inc(nextintreg);
                       dec(paralen,tcgsize2size[paraloc^.size]);
@@ -440,17 +420,10 @@ unit cpupara;
                          else
                            internalerror(2020031405);
                        end;
-                       if (side = callerside) then
+                       if side = callerside then
                          paraloc^.reference.index:=NR_STACK_POINTER_REG
                        else
-                         begin
-                           paraloc^.reference.index:=NR_FRAME_POINTER_REG;
-
-                           { create_paraloc_info_intern might be also called when being outside of
-                             code generation so current_procinfo might be not set }
-                           if assigned(current_procinfo) then
-                             txtensaprocinfo(current_procinfo).needs_frame_pointer := true;
-                         end;
+                         paraloc^.reference.index:=current_procinfo.framepointer;
 
                        paraloc^.reference.offset:=stack_offset;
 
@@ -481,7 +454,7 @@ unit cpupara;
         hp: tparavarsym;
         paraloc: pcgparalocation;
       begin
-        init_values(side,curintreg,cur_stack_offset);
+        init_values(p,side,curintreg,cur_stack_offset);
 
         result:=create_paraloc_info_intern(p,side,p.paras,curintreg,cur_stack_offset, false);
         if (p.proccalloption in cstylearrayofconst) then

+ 24 - 15
compiler/xtensa/cpupi.pas

@@ -35,19 +35,20 @@ unit cpupi;
 
     type
       txtensaprocinfo = class(tcgprocinfo)
-          callins,callxins : TAsmOp;
-          stackframesize,
-          stackpaddingreg: TSuperRegister;
-
-          needs_frame_pointer: boolean;
-          { highest N used in a call instruction }
-          maxcall : Byte;
-          // procedure handle_body_start;override;
-          // procedure after_pass1;override;            
-          constructor create(aparent: tprocinfo); override;
-          procedure set_first_temp_offset;override;
-          function calc_stackframe_size:longint;override;
-          procedure init_framepointer;override;
+        callins,callxins : TAsmOp;
+        stackframesize,
+        stackpaddingreg: TSuperRegister;
+
+        needs_frame_pointer: boolean;
+        { highest N used in a call instruction }
+        maxcall : Byte;
+        // procedure handle_body_start;override;
+        // procedure after_pass1;override;
+        constructor create(aparent: tprocinfo); override;
+        procedure set_first_temp_offset;override;
+        function calc_stackframe_size:longint;override;
+        procedure init_framepointer;override;
+        procedure generate_parameter_info;override;
       end;
 
 
@@ -152,6 +153,13 @@ unit cpupi;
       end;
 
 
+    procedure txtensaprocinfo.generate_parameter_info;
+      begin
+       tcpuprocdef(procdef).total_stackframe_size:=stackframesize;
+       inherited generate_parameter_info;
+      end;
+
+
     procedure txtensaprocinfo.init_framepointer;
       begin
         if target_info.abi=abi_xtensa_call0 then
@@ -161,8 +169,9 @@ unit cpupi;
           end
         else
           begin
-            RS_FRAME_POINTER_REG:=RS_A7;
-            NR_FRAME_POINTER_REG:=NR_A7;
+            { a frame pointer would be only needed if we do an " alloca" }
+            RS_FRAME_POINTER_REG:=RS_A15;
+            NR_FRAME_POINTER_REG:=NR_A15;
           end;
       end;
 

+ 5 - 0
compiler/xtensa/symcpu.pas

@@ -26,6 +26,7 @@ unit symcpu;
 interface
 
 uses
+  globtype,
   symconst,symtype,symdef,symsym;
 
 type
@@ -91,6 +92,10 @@ type
   tcpuprocvardefclass = class of tcpuprocvardef;
 
   tcpuprocdef = class(tprocdef)
+    { the xtensa paramanager might need to know the total size of the stackframe
+      to avoid cyclic unit dependencies or global variables, this information is
+      stored in total_stackframe_size }
+    total_stackframe_size : aint;
   end;
   tcpuprocdefclass = class of tcpuprocdef;
 

+ 197 - 38
packages/fcl-passrc/src/pasresolver.pp

@@ -563,6 +563,7 @@ type
     bfInsertArray,
     bfDeleteArray,
     bfTypeInfo,
+    bfGetTypeKind,
     bfAssert,
     bfNew,
     bfDispose,
@@ -600,6 +601,7 @@ const
     'Insert',
     'Delete',
     'TypeInfo',
+    'GetTypeKind',
     'Assert',
     'New',
     'Dispose',
@@ -1772,6 +1774,8 @@ type
     function FindUsedUnitname(const aName: string; aMod: TPasModule): TPasModule;
     procedure FinishAssertCall(Proc: TResElDataBuiltInProc;
       Params: TParamsExpr); virtual;
+    function FindSystemIdentifier(const aUnitName, aName: string;
+      ErrorEl: TPasElement): TPasElement; virtual;
     function FindSystemClassType(const aUnitName, aClassName: string;
       ErrorEl: TPasElement): TPasClassType; virtual;
     function FindSystemClassTypeAndConstructor(const aUnitName, aClassName: string;
@@ -1782,6 +1786,8 @@ type
     function FindTVarRec(ErrorEl: TPasElement): TPasRecordType; virtual;
     function GetTVarRec(El: TPasArrayType): TPasRecordType; virtual;
     function FindDefaultConstructor(aClass: TPasClassType): TPasConstructor; virtual;
+    function GetTypeInfoParamType(Param: TPasExpr;
+      out ParamResolved: TPasResolverResult; LoType: boolean): TPasType; virtual;
   protected
     // constant evaluation
     fExprEvaluator: TResExprEvaluator;
@@ -2027,6 +2033,12 @@ type
       Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
     procedure BI_TypeInfo_OnGetCallResult(Proc: TResElDataBuiltInProc;
       Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual;
+    function BI_GetTypeKind_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
+      Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
+    procedure BI_GetTypeKind_OnGetCallResult(Proc: TResElDataBuiltInProc;
+      Params: TParamsExpr; out ResolvedEl: TPasResolverResult); virtual;
+    procedure BI_GetTypeKind_OnEval(Proc: TResElDataBuiltInProc;
+      Params: TParamsExpr; Flags: TResEvalFlags; out Evaluated: TResEvalValue); virtual;
     function BI_Assert_OnGetCallCompatibility(Proc: TResElDataBuiltInProc;
       Expr: TPasExpr; RaiseOnError: boolean): integer; virtual;
     procedure BI_Assert_OnFinishParamsExpr(Proc: TResElDataBuiltInProc;
@@ -14890,13 +14902,12 @@ begin
   CreateReference(aConstructor,Params,rraRead);
 end;
 
-function TPasResolver.FindSystemClassType(const aUnitName, aClassName: string;
-  ErrorEl: TPasElement): TPasClassType;
+function TPasResolver.FindSystemIdentifier(const aUnitName, aName: string;
+  ErrorEl: TPasElement): TPasElement;
 var
   aMod, UtilsMod: TPasModule;
   SectionScope: TPasSectionScope;
   Identifier: TPasIdentifier;
-  El: TPasElement;
 begin
   Result:=nil;
 
@@ -14912,17 +14923,27 @@ begin
   // find class in interface
   if UtilsMod.InterfaceSection=nil then
     if ErrorEl<>nil then
-      RaiseIdentifierNotFound(20200523224831,aUnitName+'.'+aClassName,ErrorEl)
+      RaiseIdentifierNotFound(20200523224831,aUnitName+'.'+aName,ErrorEl)
     else
       exit;
   SectionScope:=NoNil(UtilsMod.InterfaceSection.CustomData) as TPasSectionScope;
-  Identifier:=SectionScope.FindLocalIdentifier(aClassName);
+  Identifier:=SectionScope.FindLocalIdentifier(aName);
   if Identifier=nil then
     if ErrorEl<>nil then
-      RaiseIdentifierNotFound(20200523224841,aUnitName+'.'+aClassName,ErrorEl)
+      RaiseIdentifierNotFound(20200523224841,aUnitName+'.'+aName,ErrorEl)
     else
       exit;
-  El:=Identifier.Element;
+  Result:=Identifier.Element;
+end;
+
+function TPasResolver.FindSystemClassType(const aUnitName, aClassName: string;
+  ErrorEl: TPasElement): TPasClassType;
+var
+  El: TPasElement;
+begin
+  Result:=nil;
+
+  El:=FindSystemIdentifier(aUnitName,aClassName,ErrorEl);
   if not (El is TPasClassType) then
     if ErrorEl<>nil then
       RaiseXExpectedButYFound(20180119172517,'class '+aClassName,GetElementTypeName(El),ErrorEl)
@@ -15128,6 +15149,37 @@ begin
   until false;
 end;
 
+function TPasResolver.GetTypeInfoParamType(Param: TPasExpr; out
+  ParamResolved: TPasResolverResult; LoType: boolean): TPasType;
+var
+  Decl: TPasElement;
+begin
+  Result:=nil;
+  // check type or var
+  ComputeElement(Param,ParamResolved,[rcNoImplicitProc]);
+  Decl:=ParamResolved.IdentEl;
+  if Decl=nil then exit;
+  if Decl is TPasType then
+    Result:=TPasType(Decl)
+  else if Decl is TPasVariable then
+    Result:=TPasVariable(Decl).VarType
+  else if Decl.ClassType=TPasArgument then
+    Result:=TPasArgument(Decl).ArgType
+  else if Decl.ClassType=TPasResultElement then
+    Result:=TPasResultElement(Decl).ResultType
+  else if (Decl is TPasProcedure)
+      and (TPasProcedure(Decl).ProcType is TPasFunctionType) then
+    Result:=TPasFunctionType(TPasProcedure(Decl).ProcType).ResultEl.ResultType;
+  {$IFDEF VerbosePasResolver}
+  {AllowWriteln}
+  if Result=nil then
+    writeln('TPasResolver.GetTypeInfoParamType Decl=',GetObjName(Decl),' ParamResolved=',GetResolverResultDbg(ParamResolved));
+  {AllowWriteln-}
+  {$ENDIF}
+  if LoType then
+    Result:=ResolveAliasType(Result);
+end;
+
 procedure TPasResolver.OnExprEvalLog(Sender: TResExprEvaluator;
   const id: TMaxPrecInt; MsgType: TMessageType; MsgNumber: integer;
   const Fmt: String; Args: array of {$ifdef pas2js}jsvalue{$else}const{$endif};
@@ -19974,47 +20026,18 @@ function TPasResolver.BI_TypeInfo_OnGetCallCompatibility(
 var
   Params: TParamsExpr;
   Param: TPasExpr;
-  Decl: TPasElement;
-  ParamResolved: TPasResolverResult;
   aType: TPasType;
+  ParamResolved: TPasResolverResult;
 begin
   Result:=cIncompatible;
   if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
     exit;
   Params:=TParamsExpr(Expr);
 
-  // check type or var
   Param:=Params.Params[0];
-  ComputeElement(Param,ParamResolved,[rcNoImplicitProc]);
-  Decl:=ParamResolved.IdentEl;
-  aType:=nil;
-  if (Decl<>nil) then
-    begin
-    if Decl is TPasType then
-      aType:=TPasType(Decl)
-    else if Decl is TPasVariable then
-      aType:=TPasVariable(Decl).VarType
-    else if Decl.ClassType=TPasArgument then
-      aType:=TPasArgument(Decl).ArgType
-    else if Decl.ClassType=TPasResultElement then
-      aType:=TPasResultElement(Decl).ResultType
-    else if (Decl is TPasProcedure)
-        and (TPasProcedure(Decl).ProcType is TPasFunctionType) then
-      aType:=TPasFunctionType(TPasProcedure(Decl).ProcType).ResultEl.ResultType;
-    {$IFDEF VerbosePasResolver}
-    {AllowWriteln}
-    if aType=nil then
-      writeln('TPasResolver.BI_TypeInfo_OnGetCallCompatibility Decl=',GetObjName(Decl));
-    {AllowWriteln-}
-    {$ENDIF}
-    end;
+  aType:=GetTypeInfoParamType(Param,ParamResolved,true);
   if aType=nil then
-    begin
-    {$IFDEF VerbosePasResolver}
-    writeln('TPasResolver.BI_TypeInfo_OnGetCallCompatibility ',GetResolverResultDbg(ParamResolved));
-    {$ENDIF}
     RaiseMsg(20170411100259,nTypeIdentifierExpected,sTypeIdentifierExpected,[],Param);
-    end;
   aType:=ResolveAliasType(aType);
   if not HasTypeInfo(aType) then
     RaiseMsg(20170413200118,nSymbolCannotBePublished,sSymbolCannotBePublished,[],Param);
@@ -20031,6 +20054,138 @@ begin
     FBaseTypes[btPointer],FBaseTypes[btPointer],[rrfReadable]);
 end;
 
+function TPasResolver.BI_GetTypeKind_OnGetCallCompatibility(
+  Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
+var
+  Params: TParamsExpr;
+  Param: TPasExpr;
+  aType: TPasType;
+  ParamResolved: TPasResolverResult;
+begin
+  Result:=cIncompatible;
+  if not CheckBuiltInMinParamCount(Proc,Expr,1,RaiseOnError) then
+    exit;
+  Params:=TParamsExpr(Expr);
+  Param:=Params.Params[0];
+  aType:=GetTypeInfoParamType(Param,ParamResolved,true);
+  if aType=nil then
+    RaiseMsg(20200826205441,nTypeIdentifierExpected,sTypeIdentifierExpected,[],Param);
+
+  Result:=CheckBuiltInMaxParamCount(Proc,Params,1,RaiseOnError);
+end;
+
+procedure TPasResolver.BI_GetTypeKind_OnGetCallResult(
+  Proc: TResElDataBuiltInProc; Params: TParamsExpr; out
+  ResolvedEl: TPasResolverResult);
+var
+  El: TPasElement;
+  EnumType: TPasEnumType;
+begin
+  El:=FindSystemIdentifier('system','ttypekind',Params);
+  if not (El is TPasEnumType) then
+    RaiseXExpectedButYFound(20200826211458,'enum type System.TTypeKind',GetElementTypeName(El),Params);
+  EnumType:=TPasEnumType(El);
+  SetResolverTypeExpr(ResolvedEl,btContext,EnumType,EnumType,[rrfReadable]);
+
+  if Proc=nil then ;
+end;
+
+procedure TPasResolver.BI_GetTypeKind_OnEval(Proc: TResElDataBuiltInProc;
+  Params: TParamsExpr; Flags: TResEvalFlags; out Evaluated: TResEvalValue);
+var
+  aType: TPasType;
+  El: TPasElement;
+  TypeKindType: TPasEnumType;
+  C: TClass;
+  aClass: TPasClassType;
+  bt: TResolverBaseType;
+  Value: TPasEnumValue;
+  aName: String;
+  i: Integer;
+  ParamResolved: TPasResolverResult;
+begin
+  Evaluated:=nil;
+
+  aType:=GetTypeInfoParamType(Params.Params[0],ParamResolved,true);
+  C:=aType.ClassType;
+  aName:='tkUnknown';
+  if C=TPasEnumType then
+    aName:='tkEnumeration'
+  else if C=TPasSetType then
+    aName:='tkSet'
+  else if C=TPasRecordType then
+    aName:='tkRecord'
+  else if C=TPasClassType then
+    begin
+    aClass:=TPasClassType(aType);
+    case aClass.ObjKind of
+    okObject:  aName:='tkObject';
+    okInterface:
+      case aClass.InterfaceType of
+      citCom: aName:='tkInterface';
+      else aName:='tkInterfaceRaw';
+      end;
+    okClassHelper, okRecordHelper, okTypeHelper: aName:='tkHelper';
+    else
+      aName:='tkClass';
+    end;
+    end
+  else if C=TPasClassOfType then
+    aName:='tkClassRef'
+  else if C.InheritsFrom(TPasProcedure) then
+    aName:='tkMethod'
+  else if C.InheritsFrom(TPasProcedureType) then
+    aName:='tkProcVar'
+  else
+    begin
+    bt:=ParamResolved.BaseType;
+    case bt of
+    btChar: if BaseTypeChar=btAnsiChar then aName:='tkChar' else aName:='tkWChar';
+    {$ifdef FPC_HAS_CPSTRING}
+    btAnsiChar: aName:='tkChar';
+    {$endif}
+    btWideChar: aName:='tkWideChar';
+    btString: if BaseTypeString=btAnsiString then aName:='tkAString' else aName:='tkUString';
+    {$ifdef FPC_HAS_CPSTRING}
+    btAnsiString,
+    btShortString,
+    btRawByteString: aName:='tkAString';
+    {$endif}
+    btWideString: aName:='tkWString';
+    btUnicodeString: aName:='tkUString';
+    btPointer: aName:='tkPointer';
+    {$ifdef HasInt64}
+    btQWord,
+    btInt64,
+    btComp: aName:='tkInt64';
+    {$endif}
+    else
+      if bt in btAllBooleans then
+        aName:='tkBool'
+      else if bt in btAllInteger then
+        aName:='tkInteger'
+      else if bt in btAllFloats then
+        aName:='tkFloat';
+    end;
+    end;
+
+  El:=FindSystemIdentifier('system','ttypekind',Params);
+  TypeKindType:=El as TPasEnumType;
+
+  for i:=0 to TypeKindType.Values.Count-1 do
+    begin
+    Value:=TPasEnumValue(TypeKindType.Values[i]);
+    if SameText(aName,Value.Name) then
+      begin
+      Evaluated:=TResEvalEnum.CreateValue(i,Value);
+      exit;
+      end;
+    end;
+
+  if Proc=nil then ;
+  if Flags=[] then ;
+end;
+
 function TPasResolver.BI_Assert_OnGetCallCompatibility(
   Proc: TResElDataBuiltInProc; Expr: TPasExpr; RaiseOnError: boolean): integer;
 // check params of built-in procedure 'Assert'
@@ -21684,6 +21839,10 @@ begin
     AddBuiltInProc('TypeInfo','function TypeInfo(type or var identifier): Pointer',
         @BI_TypeInfo_OnGetCallCompatibility,@BI_TypeInfo_OnGetCallResult,
         nil,nil,bfTypeInfo);
+  if bfGetTypeKind in TheBaseProcs then
+    AddBuiltInProc('GetTypeKind','function GetTypeKind(type or var identifier): System.TTypeKind',
+        @BI_GetTypeKind_OnGetCallCompatibility,@BI_GetTypeKind_OnGetCallResult,
+        @BI_GetTypeKind_OnEval,nil,bfGetTypeKind);
   if bfAssert in TheBaseProcs then
     AddBuiltInProc('Assert','procedure Assert(bool[,string])',
         @BI_Assert_OnGetCallCompatibility,nil,nil,

+ 50 - 1
packages/fcl-passrc/tests/tcresolver.pas

@@ -104,7 +104,8 @@ type
 
   TSystemUnitPart = (
     supTObject,
-    supTVarRec
+    supTVarRec,
+    supTTypeKind
     );
   TSystemUnitParts = set of TSystemUnitPart;
 
@@ -322,6 +323,7 @@ type
     Procedure TestIncStringFail;
     Procedure TestTypeInfo;
     Procedure TestTypeInfo_FailRTTIDisabled;
+    Procedure TestGetTypeKind;
 
     // statements
     Procedure TestForLoop;
@@ -2210,6 +2212,15 @@ begin
   Intf:=TStringList.Create;
   // interface
   Intf.Add('type');
+  if supTTypeKind in Parts then
+    begin
+    Intf.Add('  TTypeKind=(tkUnknown,tkInteger,tkChar,tkEnumeration,tkFloat,');
+    Intf.Add('             tkSet,tkMethod,tkSString,tkLString,tkAString,');
+    Intf.Add('             tkWString,tkVariant,tkArray,tkRecord,tkInterface,');
+    Intf.Add('             tkClass,tkObject,tkWChar,tkBool,tkInt64,tkQWord,');
+    Intf.Add('             tkDynArray,tkInterfaceRaw,tkProcVar,tkUString,tkUChar,');
+    Intf.Add('             tkHelper,tkFile,tkClassRef,tkPointer);');
+    end;
   Intf.Add('  integer=longint;');
   Intf.Add('  sizeint=int64;');
     //'const',
@@ -5065,6 +5076,44 @@ begin
   CheckResolverException(sSymbolCannotBePublished,nSymbolCannotBePublished);
 end;
 
+procedure TTestResolver.TestGetTypeKind;
+begin
+  StartProgram(true,[supTTypeKind]);
+  Add([
+  'type',
+  '  integer = longint;',
+  '  TRec = record',
+  '    v: integer;',
+  '  end;',
+  '  TClass = class of TObject;',
+  '  TObject = class',
+  '    class function ClassType: TClass; virtual; abstract;',
+  '  end;',
+  'var',
+  '  i: integer;',
+  '  s: string;',
+  '  p: pointer;',
+  '  r: TRec;',
+  '  o: TObject;',
+  '  c: TClass;',
+  '  k: TTypeKind;',
+  'begin',
+  '  k:=gettypekind(integer);',
+  '  k:=gettypekind(longint);',
+  '  k:=gettypekind(i);',
+  '  k:=gettypekind(s);',
+  '  k:=gettypekind(p);',
+  '  k:=gettypekind(r.v);',
+  '  k:=gettypekind(TObject.ClassType);',
+  '  k:=gettypekind(o.ClassType);',
+  '  k:=gettypekind(o);',
+  '  k:=gettypekind(c);',
+  '  k:=gettypekind(c.ClassType);',
+  '  k:=gettypekind(k);',
+  '']);
+  ParseProgram;
+end;
+
 procedure TTestResolver.TestForLoop;
 begin
   StartProgram(false);

+ 22 - 0
packages/pastojs/src/fppas2js.pp

@@ -2122,6 +2122,7 @@ type
     Function ConvertBuiltIn_InsertArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertBuiltIn_DeleteArray(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertBuiltIn_TypeInfo(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
+    Function ConvertBuiltIn_GetTypeKind(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertBuiltIn_Assert(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertBuiltIn_New(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
     Function ConvertBuiltIn_Dispose(El: TParamsExpr; AContext: TConvertContext): TJSElement; virtual;
@@ -4976,6 +4977,8 @@ begin
   GenMod:=nil;
   GenResolver:=nil;
 
+  // ToDo: delay only, if either RTTI or class var using a param
+
   Params:=SpecializedItem.Params;
   for i:=0 to length(Params)-1 do
     begin
@@ -10949,6 +10952,7 @@ begin
           bfInsertArray: Result:=ConvertBuiltIn_InsertArray(El,AContext);
           bfDeleteArray: Result:=ConvertBuiltIn_DeleteArray(El,AContext);
           bfTypeInfo: Result:=ConvertBuiltIn_TypeInfo(El,AContext);
+          bfGetTypeKind: Result:=ConvertBuiltIn_GetTypeKind(El,AContext);
           bfAssert:
             begin
             Result:=ConvertBuiltIn_Assert(El,AContext);
@@ -13529,6 +13533,24 @@ begin
     Result:=CreateTypeInfoRef(TypeEl,AContext,Param);
 end;
 
+function TPasToJSConverter.ConvertBuiltIn_GetTypeKind(El: TParamsExpr;
+  AContext: TConvertContext): TJSElement;
+var
+  aResolver: TPas2JSResolver;
+  Value: TResEvalValue;
+begin
+  Result:=nil;
+  aResolver:=AContext.Resolver;
+  aResolver.BI_GetTypeKind_OnEval(aResolver.BuiltInProcs[bfGetTypeKind],El,[refConst],Value);
+  try
+    if not (Value is TResEvalEnum) then
+      RaiseNotSupported(El,AContext,20200826222729,GetObjName(Value));
+    Result:=CreateLiteralNumber(El,TResEvalEnum(Value).Index);
+  finally
+    ReleaseEvalValue(Value);
+  end;
+end;
+
 function TPasToJSConverter.ConvertBuiltIn_Assert(El: TParamsExpr;
   AContext: TConvertContext): TJSElement;
 // throw pas.SysUtils.EAssertionFailed.$create("Create");

+ 39 - 0
packages/pastojs/tests/tcmodules.pas

@@ -1670,6 +1670,39 @@ begin
     begin
     Intf.AddStrings([
     'type',
+    '  TTypeKind = (',
+    '    tkUnknown,  // 0',
+    '    tkInteger,  // 1',
+    '    tkChar,     // 2 in Delphi/FPC tkWChar, tkUChar',
+    '    tkString,   // 3 in Delphi/FPC tkSString, tkWString or tkUString',
+    '    tkEnumeration, // 4',
+    '    tkSet,      // 5',
+    '    tkDouble,   // 6',
+    '    tkBool,     // 7',
+    '    tkProcVar,  // 8  function or procedure',
+    '    tkMethod,   // 9  proc var of object',
+    '    tkArray,    // 10 static array',
+    '    tkDynArray, // 11',
+    '    tkRecord,   // 12',
+    '    tkClass,    // 13',
+    '    tkClassRef, // 14',
+    '    tkPointer,  // 15',
+    '    tkJSValue,  // 16',
+    '    tkRefToProcVar, // 17  variable of procedure type',
+    '    tkInterface, // 18',
+    '    //tkObject,',
+    '    //tkSString,tkLString,tkAString,tkWString,',
+    '    //tkVariant,',
+    '    //tkWChar,',
+    '    //tkInt64,',
+    '    //tkQWord,',
+    '    //tkInterfaceRaw,',
+    '    //tkUString,tkUChar,',
+    '    tkHelper,   // 19',
+    '    //tkFile,',
+    '    tkExtClass  // 20',
+    '    );',
+    '  TTypeKinds = set of TTypeKind;',
     '  TTypeInfo = class external name ''rtl.tTypeInfo'' end;',
     '  TTypeInfoInteger = class external name ''rtl.tTypeInfoInteger''(TTypeInfo)',
     '  end;',
@@ -28478,9 +28511,12 @@ begin
   '  TColor = type TGraphicsColor;',
   'var',
   '  p: TTypeInfo;',
+  '  k: TTypeKind;',
   'begin',
   '  p:=typeinfo(TGraphicsColor);',
   '  p:=typeinfo(TColor);',
+  '  k:=GetTypeKind(TGraphicsColor);',
+  '  k:=GetTypeKind(TColor);',
   '']);
   ConvertProgram;
   CheckSource('TestRTTI_IntRange',
@@ -28492,10 +28528,13 @@ begin
     '});',
     '$mod.$rtti.$inherited("TColor", $mod.$rtti["TGraphicsColor"], {});',
     'this.p = null;',
+    'this.k = 0;',
     '']),
     LinesToStr([ // $mod.$main
     '$mod.p = $mod.$rtti["TGraphicsColor"];',
     '$mod.p = $mod.$rtti["TColor"];',
+    '$mod.k = 1;',
+    '$mod.k = 1;',
     '']));
 end;
 

+ 2 - 2
tests/test/cg/taddreal1.pp

@@ -53,11 +53,11 @@ end;
   i:=i-j;
   if trunc(i) <> trunc(89.9) then
     result := false;
-  WriteLn('Result (89.9) :',i);
+  WriteLn('Result (89.9) :',i,' trunc(i)=',trunc(i),' trunc(89.9)=',trunc(89.9));
   i:=j-i;
   if trunc(i) <> trunc(-79.9) then
     result := false;
-  WriteLn('Result (-79.9) :',i);
+  WriteLn('Result (-79.9) :',i,' trunc(i)=',trunc(i),' trunc(-79.9)=',trunc(-79.9));
   j:=j-10.0;
   if j <> 0.0 then
     result := false;