Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2018-05

Kit's ambitions!

32 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

J. Gareth Moreton

Hi everyone,

So for those who have observed me, I can be a little... excitable and optimistic sometimes with developing for Free Pascal.  Sometimes it might not be for the best, but I'd like to think that what I propose and create can me implemented relatively seamlessly.  To explain, my main speciality is in optimisation and assembly language, and this is where I feel I can contribute the most.  To explain what I'm currently looking at:

- #0033549 - Add in the x86-64 instructions PDEP and PEXT.

This has been requested by another user and seems straightforward enough to implement.  Florian mentioned that there's a standard test design for new assembler commands, but I can't for the life of me remember what it is
- can someone fill me in?

- Expand on Data Flow Analysis in the compiler.

What I personally call the "Deep Optimizer", I'm proposing an assembler-level optimisation system (although it won't touch pure assembler routines) that rearranges commands and changes registers in order to minimise pipeline stalls and to also collapse a "div" and "mod" operation into a single instruction where possible.  There are quite a few situations that can't be caught by the peephole optimiser - there's one example I've seen with "MOV", "LEA" and "MOV" being generated - if the LEA wasn't present, the peephole optimiser would make the MOVs more efficient, but because LEA is in between them, it misses the optimisation and causes a pipeline stall there due to a read-after-write penalty.

- Research possibility for 'inline' support for certain assembler routines.

For situations where speed is of the highest priority, there are some internal functions such as Int and Frac that can theoretically be inlined (a procedure call is quite expensive, around 50 cycles), but because they are written in pure assembly language, the compiler will never inline them.  I'm still working out quite a bit of theory, but I believe I will be able to allow the inlining of routines that are leaf functions (don't have CALLs of their own) and declared as 'nostackframe'.  Such a system would allow the support of 'intrinsics' that can be composed programmatically rather than as internal routines, though it's not exactly what Florian is planning. Even if Florian does go for a different approach for intrinsics, I like to think that such inline support will have uses elsewhere, especially some of the routines like "GetStackFrame" (I think) that simply return the value of RSP (if it's 'inline', which it is actually declared as in the unit, the return value will be far more accurate).

----

That's all for now.  A lot of it is personal research, but if I find any little gems, I want to share it with the world. I like playing around with mathematics and number crunching, so speed is highly sought after, and I like to improve Free Pascal so I can say it's a viable tool for the job (of course, it already is if you know what you doing).

Gareth aka. Kit

Reply by email

David Pethes
to J. Gareth Moreton

Hi,
I would welcome inlining of (simple) asm routines. Lately I wanted to use the BEXTR instruction to speed up some inlined bit reading functions. As there's no intrinsic for it and including even a simple assembly method disables inlining, it didn't go well.

As for using a BEXTR intrinsic instead: I'd like to try to add it, if it's welcomed. Judging by searching for POPCNT it shouldn't be that much work, but I'm likely to miss something - any advice is welcomed. There's at least one catch that I know of - there's no CPU target that supports BMI1 but not BMI2 (there are several such AMD cpu-s), so it should be added as well.

David

J. Gareth Moreton wrote 17 lines
- Research possibility for 'inline' support for certain assembler routines. For situations where speed is of the highest priority, there are some internal functions such as Int and Frac that can theoretically be inlined (a procedure call is quite expensive, around 50 cycles), but because they are written in pure assembly language, the compiler will never inline them.  I'm still working out quite a bit of theory, but I believe I will be able to allow the inlining of routines that are leaf functions (don't have CALLs of their own) and declared as 'nostackframe'.  Such a system would allow the support of 'intrinsics' that can be composed programmatically rather than as internal routines, though it's not exactly what Florian is planning. Even if Florian does go for a different approach for intrinsics, I like to think that such inline support will have uses elsewhere, especially some of the routines like "GetStackFrame" (I think) that simply return the value of RSP (if it's 'inline', which it is actually declared as in the unit, the return value will be far more accurate).

Reply by email

Christo
to J. Gareth Moreton: So for those who have observed me, I can be a little... excitable and optimistic…
J. Gareth Moreton wrote 6 lines
 Expand on Data Flow Analysis in the compiler. What I personally call the "Deep Optimizer", I'm proposing an assembler-level optimisation system (although it won't touch pure assembler routines) that rearranges commands and changes registers in order to minimise pipeline stalls and to also collapse a "div" and "mod" operation into a single instruction where possible.  

I would also like the data flow analyzer to look at inline assembler and emit hints and warnings if it picks up something incoherent.

- Research possibility for 'inline' support for certain assembler routines.

This has been requested before and the argument against this was that it is difficult to predict side effects of assembler routines.  I suppose if data flow analysis doesn't pick up side effects then it would be a useful feature.

I'm not a developer, but want to encourage your efforts.

Reply by email

J. Gareth Moreton

Thanks for your kind words, Christo. A lot of this is personal research, but I would like to make some elements work.  For inline support on assembler routines, I'm going to be rather conservative about what will be successful - for example, if there exists a CALL operation inside the routine, I will stop trying to inline it because there's no telling what it will do to affect the registers.

For the Deep Optimizer, I've programmed a coloured text output of an assembled procedure that I will probably make a feature that can be included with a $DEFINE in the Free Pascal source code, as the intention of it is to show what the assembled procedure is before it is optimised with data flow analysis, and then what it is after optimisation.  Alongside standard hints and warnings, it can easily be adapted to show things that look suspicious such as an uninitialised register. We'll see.

Gareth aka. Kit

Reply by email

J. Gareth Moreton

I don't know much about AVR yet, but who knows.  At the moment I'm researching how inline Pascal procedures are compiled and seeing how it can be translated into assembler routines. It might end up that I end up writing a kind of mid-level assembler within the Pascal compiler that allows the registers to be changed where possible.

Unless its stability is proven, the Deep Optimizer and the assembly inliner will be -O4 options, although I would like to get them down to -O3 ideally, especially the Deep Optimizer because that is purely at assembler level where the registers are no longer virtual, and in most cases, lines of code are being removed or rearranged rather than added.

Gareth aka. Kit

On Mon 14/05/18 18:07 , "Christo Crause" sent:
This sounds great! Perhaps some of your work will trickle down to the AVR target someday. 
Best regards, Christo

On 13 May 2018 10:31 pm, "J. Gareth Moreton" wrote: Thanks for your kind words, Christo. A lot of this is personal research, but I would like to make some elements work.  For inline support on assembler routines, I'm going to be rather conservative about what will be successful - for example, if there exists a CALL operation inside the routine, I will stop trying to inline it because there's no telling what it will do to affect the registers.

For the Deep Optimizer, I've programmed a coloured text output of an assembled procedure that I will probably make a feature that can be included with a $DEFINE in the Free Pascal source code, as the intention of it is to show what the assembled procedure is before it is optimised with data flow analysis, and then what it is after optimisation.  Alongside standard hints and warnings, it can easily be adapted to show things that look suspicious such as an uninitialised register. We'll see.

Gareth aka. Kit

Reply by email

J. Gareth Moreton

Unless I'm mistaken, Wolf, you cannot inline procedures that have asm blocks appearing anywhere (this includes the entire procedure).  Nevertheless, does the disassembly of your program show it to be inlined?

Gareth aka. Kit

Reply by email

Wolf
to David Pethes: I would welcome inlining of (simple) asm routines. Lately I wanted to use the BEXTR…
David Pethes wrote
Hi, I would welcome inlining of (simple) asm routines.

I do not know what you consider to be the existing obstacles to inlining assembler routines. What I do know is that in the attached program, inlining does work. It summarises my (current) understanding of how to measure time with nanosecond reliability
(asking for time via the Linux function "if
clock_gettime(CLOCK_MONOTONIC, @ts)=0 then" does indeed return nanoseconds, but takes some 270 ns (or about 1000 clock ticks) to execute and thus does not produce nanosecond reliability) but repeated measurements do not produce the same output, and therefore my little program does not have the reliability I want. Statistical processing does something to improve the situation, but not quite what I want.

What I can say about inlining assembler routines is this: if the
variables onto which registers are to be saved are on the stack, they
can be inlined. Never mind the hints in Lazarus' message pane. Take the
/function GetProcessorUsed: longint;    inline;//
//var//
//  ProcUsed: longint;//
//begin//
//  asm//
//    CPUID//
//    .byte 0x0F, 0x01, 0xF9      // read the Time-Stamp Counter rdtscp
(as op-code format),//
//    movl %ecx, ProcUsed      // This is the processor on which
measurements take place. Measurements on other processors are discarded.//
//  end  ['eax','ebx','ecx','edx'];//
//  GetProcessorUsed:=ProcUsed;//
//end;/
Because /ProcUsed/ is on the stack, I can move %ecx into it. But I
cannot get %ecx directly into /GetProcessorUsed/. That requires a
separate line of code.

wolf

Here is the full code, as promised. If anybody has a suggestion on how to improve it, please let me know, in a separate thread.

/program Speed_Test;
{$ASMMODE att}
uses sysutils, Linux, math;
type
   TtscCount = record
       Group: longint;
       Count: longint;
       CumFreq: Int64;
       end;
type
   TCumFreq = record
       Group: longint;
       CumFreq: real;
       end;
   TCumFrequency= array of TCumFreq;
   TTimeSpec = record
     tv_sec: int64;  //time_t;    //Seconds
     tv_nsec: int64; //clong;     //Nanoseconds
   end;
var
   TscCount: array of TtscCount;
   Measured: TCumFrequency;
   MeasurementsToDo: int64=1000000;
   ProcessorUsed: LongInt;
   Range: array[0..9999] of longint;
   ValidMeasurements: Int64;
function Get_ClockFreq(CPU: Char): real;
{Since there is no way I can find to extract actual clock frequency, I
read it from /proc/cpuinfo }
var
   FileHandle: LongInt;
   i: integer;
   Data: ansistring;
   rc:real;
   NumRead: int64;
   Buffer : packed array[0..4095] of char;
   SourceFile: AnsiString= '/proc/cpuinfo';
begin
   if not FileExists(SourceFile) then
   begin
     writeln('Error: Input file "',SourceFile,'" has not been found');
     halt;
   end;
   FileHandle:=FileOpen('/proc/cpuinfo',fmOpenRead);
   NumRead:=FileRead(FileHandle, Buffer,SizeOf(Buffer));
   Data:=Buffer[0..NumRead];
   i:=0;
   while i<=NumRead do
   begin
     inc(i);
     if CompareText(Data[i..i+8],'Processor')=0 then
     begin
       if char(Data[i+12])=CPU then
       begin
         i:=i+12;
         repeat inc(i); until CompareText(Data[i..i+6],'cpu MHz')=0 ;
         try
           rc:=StrToFloat(Data[i+11..i+18]);
         except
         on E : exception do
           begin
             writeln('Data read error: cannot convert
',Data[i+11..i+18],' into number');
             writeln('Program aborted');
             halt;
           end;
         end;
         break;
       end;
     end;
   end;
   FileClose(FileHandle);
   Get_ClockFreq:=rc;
end;

procedure ReadProcessorFrequencyInformationLeaf;  inline; var
  CPUID_16H_AX: Word;      // Processor Base Frequency (in MHz)   CPUID_16H_BX: Word;      // Maximum Frequency (in MHz)   CPUID_16H_CX: Word;      // Bus (Reference) frequency (in MHz)   CPUID_16H_DX: Word;      // Reserved = 0
begin
  CPUID_16H_AX:=0;
  CPUID_16H_BX:=0;
  CPUID_16H_CX:=0;
  asm
    mov $0x16, %eax               // select Processor Frequency Information Leaf 0x16
    cpuid                         // access it     mov %ax, CPUID_16H_AX         // Processor Base Frequency (in MHz)     mov %bx, CPUID_16H_BX         // Maximum Frequency (in MHz)     mov %cx, CPUID_16H_CX         // Bus (Reference) frequency (in MHz)     mov %dx, CPUID_16H_DX      // Reserved = 0   end  ['ax','bx','cx','dx'];
end;

function GetProcessorUsed: longint;    inline;
var
   ProcUsed: longint;
begin
   asm
     CPUID
     .byte 0x0F, 0x01, 0xF9      // read the Time-Stamp Counter rdtscp
(as op-code format),
     movl %ecx, ProcUsed    // This is the processor on which
measurements take place. Measurements on other processors are discarded.
   end  ['eax','ebx','ecx','edx'];
   GetProcessorUsed:=ProcUsed;
end;
procedure MeasureCode;
var
   ts: TTimeSpec;
   MilliSecondTime: extended;
   AX, BX, CX: Word;
   Start,Stop,i,k,l: int64;   // saves starting value from the Time
Stamp counter
   Hi: int64;
   x:real;
   y: real=2;
   ProcessorUsed_Start, ProcessorUsed_Stop, ProcUsed: longint;
   IA32_TSC_AUX_Base,IA32_TSC_AUX_Core: longint;     // content of
IA32_TSC_AUX MSR register = which CPU?
   Clock_denominator: Cardinal;
   Clock_numerator: Cardinal;
   CPUID_15H_ECX, CPUID_15H_EDX: Cardinal;     // reserved = 0
   ProcessorBaseFrequency: Word;
begin
   for i:=0 to High(Range) do Range[i]:=0;
   Start:=0;   Stop:=0;
     for k:=0 to 4 do ReadProcessorFrequencyInformationLeaf;   // this
loop is just for warm-up
   ProcessorUsed:=GetProcessorUsed;
   for i:=1 to MeasurementsToDo do
   begin
     Start:=0;   Stop:=0;
     asm
       cpuid                  // force serialization
       .byte 0x0F, 0x01, 0xF9 // read the Time-Stamp Counter rdtscp (as
op-code format),
       movl %eax, Start+0     // save least-significant longword
       movl %edx, Start+4     // save most-significant longword
       movl %ecx, ProcessorUsed_Start
     end  ['eax','ebx','ecx','edx'];
     // insert instruction to be tested below this line

if clock_gettime(CLOCK_MONOTONIC, @ts)=0 then    // return time in milliseconds, rounded to 1 nanosecond
MilliSecondTime:=RoundTo(1e3*ts.tv_sec+1e-6*ts.tv_nsec,-6);

// insert instruction to be tested above this line     asm
      .byte 0x0F, 0x01, 0xF9 // read the Time-Stamp Counter rdtscp (as op-code format),
      movl %eax, Stop+0      // save least-significant longword       movl %edx, Stop+4      // save most-significant longword       movl %ecx, ProcessorUsed_Stop
      cpuid
    end  ['eax','ebx','ecx','edx'];
    if (ProcessorUsed_Start=ProcessorUsed) and
(ProcessorUsed_Stop=ProcessorUsed) then   // ignore measurements that were not done on ProcessorUsed
      if (Stop-Start<High(Range)) then inc(Range[Stop-Start]) else inc(Range[High(Range)]);  // build cumulative frequency array   end;
end;

function AccumulateValidMeasurements: Int64;
var
   i: int64;
   Hi: int64;
begin
   ValidMeasurements:=0;
   for i:=1 to High(Range) do ValidMeasurements:=ValidMeasurements+Range[i];
   AccumulateValidMeasurements:=ValidMeasurements;
   SetLength(TscCount,1);
   TscCount[0].Group:=0;
   TscCount[0].CumFreq:=Range[0];
   for i:=0 to High(Range)-1 do
   begin
     if Range[i]>0 then
     begin
       Hi:=High(TscCount)+1;
       SetLength(TscCount,Hi+1);
       TscCount[Hi].Group:=i;
       TscCount[Hi].Count:=Range[i];
       TscCount[Hi].CumFreq:=Range[i]+TscCount[Hi-1].CumFreq;
     end;
   end;
end;
procedure ShowMeasurements;
var
   i: int64;
begin
   for i:=1 to High(TscCount) do
   begin
   writeln(TscCount[i].Group,'   ',TscCount[i].Count,'
',TscCount[i].CumFreq,'   ',100*Measured[i].CumFreq:6:3);
   if Measured[i].Cumfreq>0.999 then exit;
   end;
end;
procedure EvaluateMeasurements;
var
   i: int64;
begin
   SetLength(Measured,Length(TscCount));
   Measured[0].Group:=0;
   Measured[0].CumFreq:=TscCount[0].CumFreq/ValidMeasurements;
   for i:=0 to High(TscCount) do
   begin
     Measured[i].Group:=TscCount[i].Group;
     Measured[i].CumFreq:=TscCount[i].Cumfreq/ValidMeasurements;
   end;
end;
function Limit(CF: TCumFrequency; Frequency: real):real;  // do linear
interpolation between two points of cumulative frequency curve
var
   i: int64;
   Slope,Intercept: real;
begin
   i:=0;
   while (CF[i].CumFreq<= Frequency) do inc(i);
Intercept:=(CF[i-1].Group*CF[i].CumFreq-CF[i].Group*CF[i-1].CumFreq)
/(CF[i].CumFreq-CF[i-1].CumFreq);
Slope:=(CF[i].Group-CF[i-1].Group)/(CF[i].CumFreq-CF[i-1].CumFreq);
   Limit:=Frequency*Slope+Intercept;
end;
procedure Difference;
begin
   writeln;
   writeln('Clock ticks used:  ');
   writeln('1% Limit=',Limit(Measured,0.01):6:2);
   writeln('5% Limit=',Limit(Measured,0.05):6:2);
   writeln('20% Limit=',Limit(Measured,0.20):6:2);
   writeln('Median=',Limit(Measured,0.50):6:2);
   writeln('80% Limit=',Limit(Measured,0.80):6:2);
   writeln('95% Limit=',Limit(Measured,0.95):6:2);
   writeln('99% Limit=',Limit(Measured,0.99):6:2);
end;
begin
  // taskset -c 1;               // taskset allows to change processor,
if used from the command line
   MeasureCode;
   ValidMeasurements:=AccumulateValidMeasurements;      //
   writeln('Tests done on processor ',ProcessorUsed,', running at
',Get_ClockFreq(IntToStr(ProcessorUsed)[1]):7:3,'MHz',' doing
',ValidMeasurements,' valid measurements');      //
   EvaluateMeasurements;
   ShowMeasurements;
   Difference;
end./
_______________________________________________ fpc-devel maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply by email

David Pethes
to Wolf

Hi,

Wolf wrote 8 lines
On 14/05/2018 04:30, David Pethes wrote: > Hi, > I would welcome inlining of (simple) asm routines. > I do not know what you consider to be the existing obstacles to inlining assembler routines.

That this doesn't get inlined :) (FPC trunk, few weeks old, Win64)

function do_bextr(bits, extr: integer): integer; assembler;
nostackframe; inline;
asm
  bextr eax, ecx, edx
end;

As per your advice (if I get it correctly) this doesn't either:

function do_bextr(bits, extr: integer): integer; inline;
var foo:integer;
begin
  asm
    mov ecx, bits
    mov edx, extr
    bextr eax, ecx, edx
    mov foo, eax
  end['rax', 'rcx', 'rdx'];
  result := foo;
end;

Ad your measurement function - looks complicated, but I don't know what's the scenario. Though if you're already at the level of measuring ticks and the function doesn't take more than few 100s of thousands of ticks, rdtsc is ok. Also, be sure to measure in the real program as well
- measuring and optimizing in isolation under different conditions can be misguiding.

FFmpeg used something like this:
https://github.com/dpethes/fevh264/blob/master/core/bench.inc - place start/stop_timer around your measured function and run at least few hundreds of runs, until it stabilizes. It skips some outliers, which your code seems to do as well, but under different conditions. Advantages: dead simple, easy to integrate
Disadv.: no instruction serialization (cpuid)
So it's good for relative comparisons (did I make the function faster/slower?), but not for absolute tick count (how many ticks does this _precisely_ take?).

David

Wolf wrote 314 lines
On 14/05/2018 04:30, David Pethes wrote: > Hi, > I would welcome inlining of (simple) asm routines. > I do not know what you consider to be the existing obstacles to inlining assembler routines. What I do know is that in the attached program, inlining does work. It summarises my (current) understanding of how to measure time with nanosecond reliability (asking for time via the Linux function "if clock_gettime(CLOCK_MONOTONIC, @ts)=0 then" does indeed return nanoseconds, but takes some 270 ns (or about 1000 clock ticks) to execute and thus does not produce nanosecond reliability) but repeated measurements do not produce the same output, and therefore my little program does not have the reliability I want. Statistical processing does something to improve the situation, but not quite what I want. What I can say about inlining assembler routines is this: if the variables onto which registers are to be saved are on the stack, they can be inlined. Never mind the hints in Lazarus' message pane. Take the /function GetProcessorUsed: longint;    inline;// //var// //  ProcUsed: longint;// //begin// //  asm// //    CPUID// //    .byte 0x0F, 0x01, 0xF9      // read the Time-Stamp Counter rdtscp (as op-code format),// //    movl %ecx, ProcUsed      // This is the processor on which measurements take place. Measurements on other processors are discarded.// //  end  ['eax','ebx','ecx','edx'];// //  GetProcessorUsed:=ProcUsed;// //end;/ Because /ProcUsed/ is on the stack, I can move %ecx into it. But I cannot get %ecx directly into /GetProcessorUsed/. That requires a separate line of code. wolf Here is the full code, as promised. If anybody has a suggestion on how to improve it, please let me know, in a separate thread. /program Speed_Test; {$ASMMODE att} uses sysutils, Linux, math; type   TtscCount = record       Group: longint;       Count: longint;       CumFreq: Int64;       end; type   TCumFreq = record       Group: longint;       CumFreq: real;       end;   TCumFrequency= array of TCumFreq;   TTimeSpec = record     tv_sec: int64;  //time_t;    //Seconds     tv_nsec: int64; //clong;     //Nanoseconds   end; var   TscCount: array of TtscCount;   Measured: TCumFrequency;   MeasurementsToDo: int64=1000000;   ProcessorUsed: LongInt;   Range: array[0..9999] of longint;   ValidMeasurements: Int64; function Get_ClockFreq(CPU: Char): real; {Since there is no way I can find to extract actual clock frequency, I read it from /proc/cpuinfo } var   FileHandle: LongInt;   i: integer;   Data: ansistring;   rc:real;   NumRead: int64;   Buffer : packed array[0..4095] of char;   SourceFile: AnsiString= '/proc/cpuinfo'; begin   if not FileExists(SourceFile) then   begin     writeln('Error: Input file "',SourceFile,'" has not been found');     halt;   end;   FileHandle:=FileOpen('/proc/cpuinfo',fmOpenRead);   NumRead:=FileRead(FileHandle, Buffer,SizeOf(Buffer));   Data:=Buffer[0..NumRead];   i:=0;   while i<=NumRead do   begin     inc(i);     if CompareText(Data[i..i+8],'Processor')=0 then     begin       if char(Data[i+12])=CPU then       begin         i:=i+12;         repeat inc(i); until CompareText(Data[i..i+6],'cpu MHz')=0 ;         try           rc:=StrToFloat(Data[i+11..i+18]);         except         on E : exception do           begin             writeln('Data read error: cannot convert ',Data[i+11..i+18],' into number');             writeln('Program aborted');             halt;           end;         end;         break;       end;     end;   end;   FileClose(FileHandle);   Get_ClockFreq:=rc; end; procedure ReadProcessorFrequencyInformationLeaf;  inline; var   CPUID_16H_AX: Word;      // Processor Base Frequency (in MHz)   CPUID_16H_BX: Word;      // Maximum Frequency (in MHz)   CPUID_16H_CX: Word;      // Bus (Reference) frequency (in MHz)   CPUID_16H_DX: Word;      // Reserved = 0 begin   CPUID_16H_AX:=0;   CPUID_16H_BX:=0;   CPUID_16H_CX:=0;   asm     mov $0x16, %eax               // select Processor Frequency Information Leaf 0x16     cpuid                         // access it     mov %ax, CPUID_16H_AX         // Processor Base Frequency (in MHz)     mov %bx, CPUID_16H_BX         // Maximum Frequency (in MHz)     mov %cx, CPUID_16H_CX         // Bus (Reference) frequency (in MHz)     mov %dx, CPUID_16H_DX      // Reserved = 0   end  ['ax','bx','cx','dx']; end; function GetProcessorUsed: longint;    inline; var   ProcUsed: longint; begin   asm     CPUID     .byte 0x0F, 0x01, 0xF9      // read the Time-Stamp Counter rdtscp (as op-code format),     movl %ecx, ProcUsed    // This is the processor on which measurements take place. Measurements on other processors are discarded.   end  ['eax','ebx','ecx','edx'];   GetProcessorUsed:=ProcUsed; end; procedure MeasureCode; var   ts: TTimeSpec;   MilliSecondTime: extended;   AX, BX, CX: Word;   Start,Stop,i,k,l: int64;   // saves starting value from the Time Stamp counter   Hi: int64;   x:real;   y: real=2;   ProcessorUsed_Start, ProcessorUsed_Stop, ProcUsed: longint;   IA32_TSC_AUX_Base,IA32_TSC_AUX_Core: longint;     // content of IA32_TSC_AUX MSR register = which CPU?   Clock_denominator: Cardinal;   Clock_numerator: Cardinal;   CPUID_15H_ECX, CPUID_15H_EDX: Cardinal;     // reserved = 0   ProcessorBaseFrequency: Word; begin   for i:=0 to High(Range) do Range[i]:=0;   Start:=0;   Stop:=0;     for k:=0 to 4 do ReadProcessorFrequencyInformationLeaf;   // this loop is just for warm-up   ProcessorUsed:=GetProcessorUsed;   for i:=1 to MeasurementsToDo do   begin     Start:=0;   Stop:=0;     asm       cpuid                  // force serialization       .byte 0x0F, 0x01, 0xF9 // read the Time-Stamp Counter rdtscp (as op-code format),       movl %eax, Start+0     // save least-significant longword       movl %edx, Start+4     // save most-significant longword       movl %ecx, ProcessorUsed_Start     end  ['eax','ebx','ecx','edx'];     // insert instruction to be tested below this line if clock_gettime(CLOCK_MONOTONIC, @ts)=0 then    // return time in milliseconds, rounded to 1 nanosecond MilliSecondTime:=RoundTo(1e3*ts.tv_sec+1e-6*ts.tv_nsec,-6); // insert instruction to be tested above this line     asm       .byte 0x0F, 0x01, 0xF9 // read the Time-Stamp Counter rdtscp (as op-code format),       movl %eax, Stop+0      // save least-significant longword       movl %edx, Stop+4      // save most-significant longword       movl %ecx, ProcessorUsed_Stop       cpuid     end  ['eax','ebx','ecx','edx'];     if (ProcessorUsed_Start=ProcessorUsed) and (ProcessorUsed_Stop=ProcessorUsed) then   // ignore measurements that were not done on ProcessorUsed       if (Stop-Start<High(Range)) then inc(Range[Stop-Start]) else inc(Range[High(Range)]);  // build cumulative frequency array   end; end; function AccumulateValidMeasurements: Int64; var   i: int64;   Hi: int64; begin   ValidMeasurements:=0;   for i:=1 to High(Range) do ValidMeasurements:=ValidMeasurements+Range[i];   AccumulateValidMeasurements:=ValidMeasurements;   SetLength(TscCount,1);   TscCount[0].Group:=0;   TscCount[0].CumFreq:=Range[0];   for i:=0 to High(Range)-1 do   begin     if Range[i]>0 then     begin       Hi:=High(TscCount)+1;       SetLength(TscCount,Hi+1);       TscCount[Hi].Group:=i;       TscCount[Hi].Count:=Range[i];       TscCount[Hi].CumFreq:=Range[i]+TscCount[Hi-1].CumFreq;     end;   end; end; procedure ShowMeasurements; var   i: int64; begin   for i:=1 to High(TscCount) do   begin   writeln(TscCount[i].Group,'   ',TscCount[i].Count,'   ',TscCount[i].CumFreq,'   ',100*Measured[i].CumFreq:6:3);   if Measured[i].Cumfreq>0.999 then exit;   end; end; procedure EvaluateMeasurements; var   i: int64; begin   SetLength(Measured,Length(TscCount));   Measured[0].Group:=0;   Measured[0].CumFreq:=TscCount[0].CumFreq/ValidMeasurements;   for i:=0 to High(TscCount) do   begin     Measured[i].Group:=TscCount[i].Group;     Measured[i].CumFreq:=TscCount[i].Cumfreq/ValidMeasurements;   end; end; function Limit(CF: TCumFrequency; Frequency: real):real;  // do linear interpolation between two points of cumulative frequency curve var   i: int64;   Slope,Intercept: real; begin   i:=0;   while (CF[i].CumFreq<= Frequency) do inc(i);   Intercept:=(CF[i-1].Group*CF[i].CumFreq-CF[i].Group*CF[i-1].CumFreq) /(CF[i].CumFreq-CF[i-1].CumFreq);   Slope:=(CF[i].Group-CF[i-1].Group)/(CF[i].CumFreq-CF[i-1].CumFreq);   Limit:=Frequency*Slope+Intercept; end; procedure Difference; begin   writeln;   writeln('Clock ticks used:  ');   writeln('1% Limit=',Limit(Measured,0.01):6:2);   writeln('5% Limit=',Limit(Measured,0.05):6:2);   writeln('20% Limit=',Limit(Measured,0.20):6:2);   writeln('Median=',Limit(Measured,0.50):6:2);   writeln('80% Limit=',Limit(Measured,0.80):6:2);   writeln('95% Limit=',Limit(Measured,0.95):6:2);   writeln('99% Limit=',Limit(Measured,0.99):6:2); end; begin  // taskset -c 1;               // taskset allows to change processor, if used from the command line   MeasureCode;   ValidMeasurements:=AccumulateValidMeasurements;      //   writeln('Tests done on processor ',ProcessorUsed,', running at ',Get_ClockFreq(IntToStr(ProcessorUsed)[1]):7:3,'MHz',' doing ',ValidMeasurements,' valid measurements');      //   EvaluateMeasurements;   ShowMeasurements;   Difference; end./ > _______________________________________________ > fpc-devel maillist - > http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply by email

Wolf
to J. Gareth Moreton: Unless I'm mistaken, Wolf, you cannot inline procedures that have asm blocks appearing…

Hi Gareth,

This is the disassembly of /function GetProcessorUsed: longint;    inline; /Unless you advise me otherwise, I take the absence of /function GetProcessorUsed: longint;    inline; /mentioned anywhere in this screen print that /GetProcessorUsed/ is indeed inlined. And in the face of your incredulity, I need to remind you that I get all the compiler complaints about inlining unless  I restrict memory operations to the local stack, as outlined in my original message.

Wolf

J. Gareth Moreton wrote 10 lines
Unless I'm mistaken, Wolf, you cannot inline procedures that have asm blocks appearing anywhere (this includes the entire procedure).  Nevertheless, does the disassembly of your program show it to be inlined? Gareth aka. Kit _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply by email

Thorsten Engler
to Wolf

From: fpc-devel On Behalf Of Wolf
Sent: Friday, 18 May 2018 07:27

This is the disassembly of function GetProcessorUsed: longint; inline; Unless you advise me otherwise, I take the absence of function GetProcessorUsed: longint; inline; mentioned anywhere in this screen print that GetProcessorUsed is indeed inlined. And in the face of your incredulity, I need to remind you that I get all the compiler complaints about inlining unless I restrict memory operations to the local stack, as outlined in my original message.

The line numbers mentioned in your disassembly screen shot don’t line up with the source code you’ve previously posted, and the screenshot doesn’t show enough context to say anything about this being either simply the disassembly of the GetProcessorUsed function or really a place where you called GetProcessorUsed in source code and the compiler inlined the call.

But given the fact that you can see it building a stack frame, I would strongly suspect that you are simply showing the disassembly of GetProcessorUsed here instead of a call site which is calling GetProcessorUsed and has the call inlined.

Reply by email

J. Gareth Moreton

What you've shown suggests that the
routine is NOT inlined, as it's building a
stack frame and the 'add' operations at
the very top looks like padding (they're
all zeroes in machine code) to align the
procedure to a 16 byte boundary, and would
crash the program if directly executed.

Look at the disassembly where your
function is called - the presence of CALL
will tell you it is not inlined.

Gareth aka. Kit

Reply by email

J. Gareth Moreton

Would you object to me trying anyway, Florian? It might be that I run into the same problems you had and it's too unsafe, but I'm going by a conservative philosophy in that if it spots something that it can't work out (e.g. an instruction that it's not programmed to handle) or is potentially unsafe (e.g. reading and writing to a block of memory that it doesn't have control over, due to multi-threading issues), then it just stops optimising and drops all assumptions that it has made at that point.

As a small test case, I'm attempting to see if I can spot and optimise, for example, "mov %rax, %rbx; lea %rcx, -8(%rsp); mov %rbx, 8(%rsp)", where a pipeline stall occurs due to a read-after-write penalty (with %rbx in this case).  Normally the peephole optimiser will sort out such a problem, but because LEA appears in between the MOV's, it won't catch it.  The other thing is attempting to merge unsigned division and modulus into a single "div" operation - that was actually my intention behind "Minor_div_improvement" in issue #32984 - by changing SBB to SETAE, it preserves the flags register that I can make better use of it in such a deep optimiser, as I can be certain as to what its value is at that point (or at the very least know it hasn't changed).
More than anything, it's a personal research project - I'd be over the moon if it works and it holds up to every test case thrown at it, but if it doesn't, well, at least I know more about assembly language and have gained experience to improve the peephole optimiser and other elements of FPC.

Gareth aka. Kit

Christo wrote
On Sun, 2018-05-13 at 03:28 +0100, J. Gareth Moreton wrote: >  Expand on Data Flow Analysis in the compiler. > > What I personally call the "Deep Optimizer", I'm proposing an

assembler-level optimisation

> system (although it won't touch pure assembler routines) that

rearranges commands and changes

> registers in order to minimise pipeline stalls and to also collapse a

"div" and "mod"

> operation into a single instruction where possible.   I would also like the data flow analyzer to look at inline assembler and

emit hints and warnings

if it picks up something incoherent.

We had already a data flow analyzer for assembler in 1.0.x and early 2.x times, however, it was
disabled after several years as it was too hard to make it work safely.

Reply by email

Florian Klämpfl
to Christo: I would also like the data flow analyzer to look at inline assembler and emit hints and…
Christo wrote 10 lines
On Sun, 2018-05-13 at 03:28 +0100, J. Gareth Moreton wrote: >  Expand on Data Flow Analysis in the compiler. > > What I personally call the "Deep Optimizer", I'm proposing an assembler-level optimisation > system (although it won't touch pure assembler routines) that rearranges commands and changes > registers in order to minimise pipeline stalls and to also collapse a "div" and "mod" > operation into a single instruction where possible.   I would also like the data flow analyzer to look at inline assembler and emit hints and warnings if it picks up something incoherent.

We had already a data flow analyzer for assembler in 1.0.x and early 2.x times, however, it was disabled after several years as it was too hard to make it work safely.

Reply by email

Florian Klämpfl
to J. Gareth Moreton: Would you object to me trying anyway, Florian? It might be that I run into the same…
J. Gareth Moreton wrote
Would you object to me trying anyway, Florian?

No, feel free to go ahead, but it needs to be done step by step.

Show quoted text 8 lines
It might be that I run into the same problems you had and it's too unsafe, but I'm going by a conservative philosophy in that if it spots something that it can't work out (e.g. an instruction that it's not programmed to handle) or is potentially unsafe (e.g. reading and writing to a block of memory that it doesn't have control over, due to multi-threading issues), then it just stops optimising and drops all assumptions that it has made at that point. As a small test case, I'm attempting to see if I can spot and optimise, for example, "mov %rax, %rbx; lea %rcx, -8(%rsp); mov %rbx, 8(%rsp)", where a pipeline stall occurs due to a read-after-write penalty (with %rbx in this case).

Things like this are fine, it gets hairy though as soon as memory locations are involved.

Reply by email

J. Gareth Moreton

So far, I'm researching the optimisation as listed below... tracking registers with identical values and changing them to minimise pipeline stalls.  Because I don't need to keep track of their actual values, just whether they've changed since a particular MOV instruction, I've managed to move this into the peephole optimiser as an extension to TX86AsmOptimizer.PostPeepholeOptMov().

It's a bit more difficult than it looks though - I've had a lot of crashes so far when it changes a register when it shouldn't do, but I'm ironing out the bugs one by one.  To truly see the gains though, one would need to perform some kind of intense timing comparison.

This would be the first step in the step-by-step implementation.  More in-depth deep data-flow optimisation, like successfully merging div and mod instructions of the same numerator and denominator will require some more care and thought, especially as the two divison operations may not use the same registers (if successful though, it will improve the compiler itself, since it has "x div 1000" and "x mod 1000" side-by-side in a couple of places, a common pair of expressions to produce a human-readable time metric, e.g. seconds and milliseconds).
Gareth aka. Kit

On Sun 03/06/18 14:12 , Florian Klämpfl sent:

J. Gareth Moreton wrote
Would you object to me trying anyway, Florian?

No, feel free to go ahead, but it needs to be done step by step.

It might be that I run into the same problems you had and it's too unsafe, but I'm going by a conservative philosophy in that if it spots

something that it can't work out (e.g. an

instruction that it's not programmed to handle) or is potentially unsafe

(e.g. reading and writing to a block of memory

that it doesn't have control over, due to multi-threading issues), then

it just stops optimising and drops all

assumptions that it has made at that point. As a small test case, I'm attempting to see if I can spot and optimise,

for example, "mov %rax, %rbx; lea %rcx,

-8(%rsp); mov %rbx, 8(%rsp)", where a pipeline stall occurs due to a

read-after-write penalty (with %rbx in this case).

Things like this are fine, it gets hairy though as soon as memory locations are involved.

Reply by email

J. Gareth Moreton

So a progress update.

I've tied in part of my deep optimiser
into the peephole optimiser, specifically
PostPeepholeOptMov, and it's had some
unexpected benefits. One of the things it
does is start with a MOV command that
copies a register's contents into another,
then looks at subsequent reference
addresses to see if it can swap out one
register for another, to reduce the chance
of a pipeline stall. There are cases where
it's noticed that all such registers have
been switched in a certain block and hence
safely removes the original MOV command.

What this means is that as well as
reducing the chances of a pipeline stall,
it's removing unnecessary assignments.

My main test case has been compiling the
compiler, since it's sufficiently complex
and easy to crash if incorrect machine
code is produced, and it also gives plenty
of examples of optimisation. As a very
brief example, in
compiler/x86_64/symcpu.pas in
TCPUProcDef.ppuload_platform, the first
four lines are:

movq %rcx,%rax
movq %rdx,%rsi
movq %rax,%rbx
movq %rbx,%rcx

The deep optimiser changes this to:

movq %rcx,%rax
movq %rdx,%rsi
movq %rcx,%rbx

It determines, for the third MOV, it can
change %rax for %rcx to minimise a
pipeline stall, and then knows that %rbx
and %rcx contain the same value, so can
remove the 4th MOV completely. Given that
modern processors usually have at least 3
ALUs and the interdependencies have been
removed, this will likely give a speed
increase of one cycle over these few
commands.

Before I go submitting patches though, I
still need to test it under Linux and
i386.

Kit

Reply by email

David Pethes
to J. Gareth Moreton

Hi,
nice work.

J. Gareth Moreton wrote 16 lines
The deep optimiser changes this to: movq %rcx,%rax movq %rdx,%rsi movq %rcx,%rbx It determines, for the third MOV, it can change %rax for %rcx to minimise a pipeline stall, and then knows that %rbx and %rcx contain the same value, so can remove the 4th MOV completely. Given that modern processors usually have at least 3 ALUs and the interdependencies have been removed, this will likely give a speed increase of one cycle over these few commands.

Note that modern cpu-s can use move elimination for reg to reg moves, so it doesn't cost any execution resources (it's "free"). Despite that it's still a win, because it spares both bytes in I-cache and decoder bandwidth (which can indirectly lead to some spared cycle(s) at other places).

David

Reply by email

J. Gareth Moreton

Thanks David,

I'm still learning some of the nuances of the Intel and AMD processors, but most of it is just logical analysis.  Admittedly my main drive has been to shrink down the size of the binary, since Delphi and Free Pascal have always been a little bit bloated in comparison.  Not that it is necessarily a bad thing, but saving space without sacrificing performance can only be a good thing, especially for those with limited bandwidth or for saving those few precious bytes when burning files to a CD or DVD.

There have been a few instances in the compiled compiler (my main test case) where an entire register is freed up due to my deep optimisation, and that means the corresponding "push" and "pop" at either end of the procedure can be removed (along with the corresponding stack unwinding information), although I haven't started programming that yet.

I am ready to submit this part of my deep optimiser as a patch.  I'm just waiting for Florian's acceptance or rejection of my debug strip patch - https://bugs.freepascal.org/view.php?id=33798 (the 3rd attempt!) - only because it shares some debugging code with said patch (it was useful to monitor how the registers inside references were changed).  If it's rejected, it just means I'll have to change some of that debugging code a bit.

Gareth aka. Kit

On Mon 11/06/18 20:27 , David Pethes sent:
Hi,
nice work.

J. Gareth Moreton wrote 16 lines
The deep optimiser changes this to: movq %rcx,%rax movq %rdx,%rsi movq %rcx,%rbx It determines, for the third MOV, it can change %rax for %rcx to minimise a pipeline stall, and then knows that %rbx and %rcx contain the same value, so can remove the 4th MOV completely. Given that modern processors usually have at least 3 ALUs and the interdependencies have been removed, this will likely give a speed increase of one cycle over these few commands.

Note that modern cpu-s can use move elimination for reg to reg moves, so it doesn't cost any execution resources (it's "free"). Despite that it's still a win, because it spares both bytes in I-cache and decoder bandwidth (which can indirectly lead to some spared cycle(s) at other places).

David

Reply by email

J. Gareth Moreton

Ideally yes, but this occurs after peephole optimisations where all of the register allocations have already been made.  Doing the peephole and deep optimisations while the registers are still in a virtual state would be better overall, but may require a huge overhaul of the compiler that might be asking for too much trouble.  There's also the issue that some commands only work with certain registers, and optimisations have to be careful of that fact.

Gareth

On Tue 12/06/18 22:45 , sent:

J. Gareth Moreton wrote 17 lines
Thanks David, I'm still learning some of the nuances of the Intel and AMD processors, but most of it is just logical analysis. Admittedly my main drive has been to shrink down the size of the binary, since Delphi and Free Pascal have always been a little bit bloated in comparison. Not that it is necessarily a bad thing, but saving space without sacrificing performance can only be a good thing, especially for those with limited bandwidth or for saving those few precious bytes when burning files to a CD or DVD. There have been a few instances in the compiled compiler (my main test case) where an entire register is freed up due to my deep optimisation, and that means the corresponding "push" and "pop" at either end of the procedure can be removed (along with the corresponding stack unwinding information), although I haven't started programming that yet.

Isn't it better to perform this optimization before register allocation. Then, when this happens, the corresponding "push" and "pop" wouldn't even be put by the compiler, because the register wouldn't have to be spilled.

Nikolay

Reply by email

Unknown author
to J. Gareth Moreton: I'm still learning some of the nuances of the Intel and AMD processors, but most of it is…
J. Gareth Moreton wrote 17 lines
Thanks David, I'm still learning some of the nuances of the Intel and AMD processors, but most of it is just logical analysis. Admittedly my main drive has been to shrink down the size of the binary, since Delphi and Free Pascal have always been a little bit bloated in comparison. Not that it is necessarily a bad thing, but saving space without sacrificing performance can only be a good thing, especially for those with limited bandwidth or for saving those few precious bytes when burning files to a CD or DVD. There have been a few instances in the compiled compiler (my main test case) where an entire register is freed up due to my deep optimisation, and that means the corresponding "push" and "pop" at either end of the procedure can be removed (along with the corresponding stack unwinding information), although I haven't started programming that yet.

Isn't it better to perform this optimization before register allocation. Then, when this happens, the corresponding "push" and "pop" wouldn't even be put by the compiler, because the register wouldn't have to be spilled.

Nikolay

Show quoted text 55 lines
I am ready to submit this part of my deep optimiser as a patch. I'm just waiting for Florian's acceptance or rejection of my debug strip patch - https://bugs.freepascal.org/view.php?id=33798 (the 3rd attempt!) - only because it shares some debugging code with said patch (it was useful to monitor how the registers inside references were changed). If it's rejected, it just means I'll have to change some of that debugging code a bit. Gareth aka. Kit On Mon 11/06/18 20:27 , David Pethes sent: > Hi, > nice work. > > On 8. 6. 2018 0:46, J. Gareth Moreton wrote: > > > The deep optimiser changes this to: > > > > movq %rcx,%rax > > movq %rdx,%rsi > > movq %rcx,%rbx > > > > It determines, for the third MOV, it can > > change %rax for %rcx to minimise a > > pipeline stall, and then knows that %rbx > > and %rcx contain the same value, so can > > remove the 4th MOV completely. Given that > > modern processors usually have at least 3 > > ALUs and the interdependencies have been > > removed, this will likely give a speed > > increase of one cycle over these few > > commands. > > Note that modern cpu-s can use move elimination for reg to reg > moves, so > it doesn't cost any execution resources (it's "free"). Despite that > it's > still a win, because it spares both bytes in I-cache and decoder > bandwidth (which can indirectly lead to some spared cycle(s) at > other > places). > > David > _______________________________________________ > fpc-devel maillist - > http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel">htt > p://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel > > _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply by email

J. Gareth Moreton

I haven't fully uncovered the secrets of
the compiler yet, but I did notice "pre-
peephole pass" under x86, but I think the
only functions it touched was one of the
bit shifts. Does this occur before
register allocation or was it just
something that had to be done before Pass
1?

Gareth

On Wed 13/06/18 20:29 , Florian Klämpfl
sent:

Am 12.06.2018 um 23:27 schrieb J. Gareth

Moreton:

> Ideally yes, but this occurs after

peephole

optimisations where all of the register

allocations have already been made.

> Doing the peephole and deep

optimisations while

the registers are still in a virtual

state would be better overall, but

> may require a huge overhaul of the

compiler that

might be asking for too much trouble. 

There's also the issue that

some > commands only work with certain

registers, and

optimisations have to be careful of that

fact.

This is not that hard actually. The only

difference is how register

allocations are handled. Just look at

the scheduler

pass of arm, it works also before

register allocation (and afterwards).

Reply by email

Florian Klämpfl
to Unknown author: Isn't it better to perform this optimization before register allocation. Then, when this…

Am 12.06.2018 um 23:45 schrieb

Show quoted text 23 lines
On Mon, 2018-06-11 at 21:07 +0100, J. Gareth Moreton wrote: > Thanks David, > > I'm still learning some of the nuances of the Intel and AMD > processors, but most of it is just logical analysis. Admittedly my > main drive has been to shrink down the size of the binary, since > Delphi and Free Pascal have always been a little bit bloated in > comparison. Not that it is necessarily a bad thing, but saving space > without sacrificing performance can only be a good thing, especially > for those with limited bandwidth or for saving those few precious > bytes when burning files to a CD or DVD. > > There have been a few instances in the compiled compiler (my main > test case) where an entire register is freed up due to my deep > optimisation, and that means the corresponding "push" and "pop" at > either end of the procedure can be removed (along with the > corresponding stack unwinding information), although I haven't > started programming that yet. Isn't it better to perform this optimization before register allocation. Then, when this happens, the corresponding "push" and "pop" wouldn't even be put by the compiler, because the register wouldn't have to be spilled.

Yes, this is what I already started once, a peephole optimizer pass being able to be run before register allocation which executes in particular optimizations which reduce register usage.

Reply by email

Florian Klämpfl
to J. Gareth Moreton: Ideally yes, but this occurs after peephole optimisations where all of the register…
J. Gareth Moreton wrote
Ideally yes, but this occurs after peephole optimisations where all of the register allocations have already been made. Doing the peephole and deep optimisations while the registers are still in a virtual state would be better overall, but may require a huge overhaul of the compiler that might be asking for too much trouble.  There's also the issue that some commands only work with certain registers, and optimisations have to be careful of that fact.

This is not that hard actually. The only difference is how register allocations are handled. Just look at the scheduler pass of arm, it works also before register allocation (and afterwards).

Reply by email

J. Gareth Moreton

Thanks. I'll have a study of this and potentially move my initial deep optimisation component to this stage.

I've made some more peephole optimisations in the meantime, but I'm going to hold off on posting them because they're starting to conflict with my other submissions.  Besides, I've given you far too many patches already!

Gareth

On Thu 14/06/18 21:58 , Florian Klämpfl sent:

J. Gareth Moreton wrote 8 lines
I haven't fully uncovered the secrets of the compiler yet, but I did notice "pre- peephole pass" under x86, but I think the only functions it touched was one of the bit shifts. Does this occur before register allocation or was it just something that had to be done before Pass 1?

It is only before pass 1.

I attached a patch I once started which shows the idea.

Reply by email

Florian Klämpfl
to J. Gareth Moreton: I haven't fully uncovered the secrets of the compiler yet, but I did notice "pre-…
J. Gareth Moreton wrote 8 lines
I haven't fully uncovered the secrets of the compiler yet, but I did notice "pre- peephole pass" under x86, but I think the only functions it touched was one of the bit shifts. Does this occur before register allocation or was it just something that had to be done before Pass 1?

It is only before pass 1.

I attached a patch I once started which shows the idea.

Reply by email

J. Gareth Moreton

Hi Florian,
I don't know if you have any answers, but I'm unable to apply any patches I receive. I can view them and see the changes, and manually apply them via copy+paste if I have to, but using the "Apply Patch" option ends up not doing anything.  Is there a fix to this, or does it error out because I only have read access to SVN (even though the patch should only modify my local files)?

Looking at the design though, I can definitely experiment to see how the deep optimiser performs in the preallocation block.  It will certainly have the advantage of being able to handle registers that may end up being stored on the stack due to the lack of free actual registers.  If needs be, I'll submit the current deep optimiser that does all of its work after the peephole optimisation, and can change it to pre register allocation later on.  I will need to see if it performs better or worse the earlier stage and also potentially cause other optimisations to get missed because of MOVs being changed or removed.

Fun times ahead!  Thanks for the patch.
Gareth

On Thu 14/06/18 21:58 , Florian Klämpfl sent:

J. Gareth Moreton wrote 8 lines
I haven't fully uncovered the secrets of the compiler yet, but I did notice "pre- peephole pass" under x86, but I think the only functions it touched was one of the bit shifts. Does this occur before register allocation or was it just something that had to be done before Pass 1?

It is only before pass 1.

I attached a patch I once started which shows the idea.

Reply by email

J. Gareth Moreton

Oh! I'm still a beginner with version
control, it seems!

On Fri 15/06/18 16:23 , Florian Klämpfl
sent:

Am 14.06.2018 um 23:49 schrieb J. Gareth

Moreton:

Show quoted text 6 lines
> Hi Florian, > > I don't know if you have any answers,

but I'm

unable to apply any patches I receive. I

can view them and see the

> changes, and manually apply them via

copy+paste

if I have to, but using the "Apply

Patch" option ends up not

doing > anything.  Is there a fix to this, or

does

it error out because I only have read

access to SVN (even though the patch

Show quoted text 5 lines
> should only modify my local files)? Did you try to use the patch.exe from

the command line which comes with

FPC?

Reply by email

Florian Klämpfl
to J. Gareth Moreton: I don't know if you have any answers, but I'm unable to apply any patches I receive. I…
J. Gareth Moreton wrote 6 lines
Hi Florian, I don't know if you have any answers, but I'm unable to apply any patches I receive. I can view them and see the changes, and manually apply them via copy+paste if I have to, but using the "Apply Patch" option ends up not doing anything.  Is there a fix to this, or does it error out because I only have read access to SVN (even though the patch should only modify my local files)?

Did you try to use the patch.exe from the command line which comes with FPC?

Reply by email

J. Gareth Moreton

Not much luck for me - the file won't patch without options or modifications, and using -p 1 to remove the "a/" and "b/" from the starts of the files causes an assertion in patch.exe.  Back to doing it manually for now!

Gareth

On Fri 15/06/18 16:23 , Florian Klämpfl sent:

J. Gareth Moreton wrote
Hi Florian, I don't know if you have any answers, but I'm unable to apply any

patches I receive. I can view them and see the

changes, and manually apply them via copy+paste if I have to, but using

the "Apply Patch" option ends up not doing

anything.  Is there a fix to this, or does it error out because I only

have read access to SVN (even though the patch

should only modify my local files)?

Did you try to use the patch.exe from the command line which comes with FPC?

Reply by email

Florian Klämpfl
to J. Gareth Moreton
J. Gareth Moreton wrote
Not much luck for me - the file won't patch without options or modifications, and using -p 1 to remove the "a/" and "b/" from the starts of the files causes an assertion in patch.exe.

Sorry, my bad. The patch has unix line feeds, this crashes patch.exe for windows. Try again with the attached one or convert the line endings of the first one to window ones.

Reply by email

J. Gareth Moreton

Something tells me that we should write our own patch.exe at some point to alleviate these shortcomings!  Thanks for the patch again.

Any word on what I've submitted so far? I ask because I found some new peephole optimisations that can make some good speed and size savings, but one of them requires a new Pass 1 function and will either have to be merged into the binary search list, or the large case block, so I can't submit it yet until I know which way the source tree will go. Gareth aka. Kit

On Fri 15/06/18 20:03 , Florian Klämpfl sent:

J. Gareth Moreton wrote
Not much luck for me - the file won't patch without options or

modifications, and using -p 1 to remove the "a/" and "b/"

from the starts of the files causes an assertion in patch.exe.

Sorry, my bad. The patch has unix line feeds, this crashes patch.exe for windows. Try again with the attached one or
convert the line endings of the first one to window ones.

Reply by email

J. Gareth Moreton

Sorry, I just realised that was unfairly impatient of me.  I've still got little things I can work on, but I'm worried about creating a large backlog.

Gareth

On Fri 15/06/18 21:11 , "J. Gareth Moreton"
sent:
Something tells me that we should write our own patch.exe at some point to alleviate these shortcomings!  Thanks for the patch again.

Any word on what I've submitted so far? I ask because I found some new peephole optimisations that can make some good speed and size savings, but one of them requires a new Pass 1 function and will either have to be merged into the binary search list, or the large case block, so I can't submit it yet until I know which way the source tree will go. Gareth aka. Kit

On Fri 15/06/18 20:03 , Florian Klämpfl sent:

J. Gareth Moreton wrote
Not much luck for me - the file won't patch without options or

modifications, and using -p 1 to remove the "a/" and "b/"

from the starts of the files causes an assertion in patch.exe.

Sorry, my bad. The patch has unix line feeds, this crashes patch.exe for windows. Try again with the attached one or
convert the line endings of the first one to window ones.

Reply by email