Does Delphi compiler perform optimization? - delphi

I am using Delphi 7 IDE. Does Delphi compiler optimize codes, just like what the C++ compiler is doing in this following link?
http://msdn.microsoft.com/en-us/library/aa366877(VS.85).aspx
WCHAR szPassword[MAX_PATH];
// Retrieve the password
if (GetPasswordFromUser(szPassword, MAX_PATH))
UsePassword(szPassword);
// Clear the password from memory
SecureZeroMemory(szPassword, sizeof(szPassword));
If ZeroMemory were called in this example instead of SecureZeroMemory, the compiler could optimize the call because the szPassword buffer is not read from before it goes out of scope. The password would remain on the application stack where it could be captured in a crash dump or probed by a malicious application.

Yes, of course Delphi performs optimizations. However, it does not perform the optimization that the SecureZeroMemory function is meant to circumvent. There is no need to use that function in Delphi; just use plain old ZeroMemory, or even FillChar. They're not macros, and they don't do anything that Delphi recognizes as being unused assignment statements that could get optimized out.

Delphi performs code optimization by default, you can disable it in Project > Options > Compiler.
The Delphi help provide a few tips of what type of optimizations are used:
The $O directive controls code optimization. In the {$O+} state, the compiler performs a number of code optimizations, such as placing variables in CPU registers, eliminating common subexpressions, and generating induction variables.
It also states that "the compiler performs no "unsafe" optimizations", but in the sense that they won't alter the execution path, not from a security point of view.

Delphi certainly optimizes code (it is a modern, and excellent, compiler). Another example of optimization deleting lines is:
SomeFunction(); // Set breakpoint here, then step (F10)
myInt := 7; // Next line will not hit this...
myInt := 13; // ...but will instead skip to here
I like to ensure optimization is in the correct state (and not accidentally left switched on or off) by adding {$I MyProjectOptions.inc} in every .pas file in my project. This goes just below the unit name (right at the top of the file). In "MyProjectOptions.inc" you simply add this code:
// Is this a debug or non-debug build?
{$IF Defined(DEBUG)}
{$O-} // Turn optimization off
{$ELSEIF Defined(NDEBUG)}
{$O+} // Ensure optimisation is on
{$IFEND}
Finally, ensure you have defined "DEBUG" and "NDEBUG" (or your equivalent in older versions of Delphi) in the Conditional defines section of Project > Options > Diectories/Conditionals.

I don't believe the compiler will ever eliminate apparently dead code like this. I have never had trouble setting breakpoints on code that could have been eliminated as redundant.

For some scenarios, the compiler can detect if the code is unreachable and eliminate the code.
For instance, the compiler correctly eliminates the "unreachable" portion of the code below.
It will not generate code for that line so:
So there are no blue bullets indicating there is code
Breakpoints put on that line will be marked visually as 'not reachable'
Just tested in Delphi XE, but older Delphi versions have similar behaviour.
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
procedure Test;
begin
if (True = False) then
Writeln('Unreachable')
else
Writeln('Reachable');
end;
begin
try
Test();
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
It takes quite some while to learn when (or when not) the optimizer on code level and liker level kicks in.
For instance: When you have optimizations turned on, the compiler will also eliminate variables as soon as they are not used.
Sometimes, it even eliminates global symbols.
Danny Thorpe (former Delphi Compiler engineer and Chief Scientist) once wrote a magic method Touch that prevents this.
Just call this Touch method at the end of your method to fool the optimizer during debugging:
procedure Touch(var arg);
begin
end;
--jeroen

Related

H2077 inside try finally block with goto - is it Tokyo's compiler defect?

After upgrading to 10.2 Tokyo one of third-party components started throwing a lot of exceptions. Debugging showed problematic part of code, that can be represented by this (hopefully) minimal code:
function foo(i: Integer): Boolean;
label bar;
begin
try
if i=1 then goto bar;
Result:=False;
EXIT;
bar:
Result:=True; //<~~ H2077 Value assigned to 'foo' never used with Optimization on
finally
end;
end;
With Optimization in compiler options set to
True (default for Release configuration) - foo(1) returns False
False (default for Debug configuration) - foo(1) returns True
Such problem does not occur in XE7. This answer explaining changes in Tokyo's compiler is probably related - but maybe fixing some of the problems introduced new.
My question is:
is it Tokyo's compiler defect? I am pretty sure it is, but I am new to programming in Delphi and it would be great to get confirmation from more experienced users.
If it is compiler's defect then I have a follow up question: is there any quick way to fix this code? I know how to remove goto in my MCVE with simple if then else statement, but real code is way more complicated:
if cond1 then goto bar;
if cond2 then goto bar;
if cond3 then goto bar;
...
if condN then goto bar;
And some of the if blocks also contain loops with inner goto. I know how to rewrite all of this logic to nested if then else blocks, but maybe there is an easier way to fix it without waiting for compiler's defect or third-party component to be fixed (I know any of those won't happen soon).
This is a compiler defect. foo(1) should return True.
It looks like the optimiser is confused by this particular use of goto.
Submit a bug report to Embarcadero. To get past the problem in the meantime you can:
Contact the third party component vendor and ask for a workaround, or
re-write the code to avoid the goto which appears to confuse the optimiser, or
revert to an older version of the compiler that is not defective, or
disable optimisation for any functions affected by the defect.

Why an application starts with FPU Control Word different than Default8087CW?

Could you please help me to understand what is going on with FPU Control Word in my Delphi application, on Win32 platform.
When we create a new VCL application, the control word is set up to 1372h. This is the first thing I don't understand, why it is 1372h instead of 1332h which is the Default8087CW defined in System unit.
The difference between these two:
1001101110010 //1372h
1001100110010 //1332h
is the 6th bit which according to documentation is reserved or not used.
The second question regards CreateOleObject.
function CreateOleObject(const ClassName: string): IDispatch;
var
ClassID: TCLSID;
begin
try
ClassID := ProgIDToClassID(ClassName);
{$IFDEF CPUX86}
try
Set8087CW( Default8087CW or $08);
{$ENDIF CPUX86}
OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER, IDispatch, Result));
{$IFDEF CPUX86}
finally
Reset8087CW;
end;
{$ENDIF CPUX86}
except
on E: EOleSysError do
raise EOleSysError.Create(Format('%s, ProgID: "%s"',[E.Message, ClassName]),E.ErrorCode,0) { Do not localize }
end;
end;
The above function is changing control word to 137Ah, so it is turning on the 3rd bit (Overflow Mask). I don't understand why it is calling Reset8087CW after, instead of restoring the state of the word which was before entering into the function?
The 6th bit is reserved and ignored. Those two control words are in fact equal in the sense that the FPU behaves the same. The system just happens to set the reserved bit. Even if you attempt to set the value to $1332, the system will set it to $1372. No matter what value you ask the 6th bit to have, it will always be set. So, when comparing these values you have to ignore that bit. Nothing to worry about here.
As for CreateOleObject the authors decided that if you are going to use that function then you are also going to mask overflow when using the COM object, and indeed beyond. Who knows why they did so, and for 32 bit code only? Probably they found a bunch of COM objects that routinely overflowed, and so added this sticking plaster. It wasn't enough to mask overflow on creation, it also need to be done when using the object so The RTL designers chose to unmask overflow henceforth.
Or perhaps it was a bug. They decided not to fix it for 32 bit code because people relied on the behaviour, but they did fix for 64 bit code.
In any case this function does nothing very special. You don't need to use it. You can write your own that does what you want it to do.
Floating point control is a problem when working with interop. Delphi code expects unmasked exceptions. Code built with other tools typically masks them. Ideally you would mask exceptions when you call out of your Delphi code and unmask them on return. Expect other libraries to arbitrarily change the control word. Also be aware that Set8087CW is not thread safe which is a massive problem that Embarcadero have refused to address for many years.
There's no easy way forward. If you aren't using floating point in your program then you could simply mask exceptions and probably be fine. Otherwise you need to make sure that the control word is set appropriately at all points in all threads. In general that is close to impossible using the standard Delphi RTL. I personally handle this by replacing the key parts of the RTL with threadsafe versions. I have documented how to do so in this QC report: QC#107411.
Disclaimer: I debugged the questions in Delphi XE.
First, the second question.
If you look at the code of Set8087CW you will see that it stores the new FPU CW value in Default8087CW variable, and Reset8087CW restores FPU CW from Default8087CW; so the Reset8087CW call after Set8087CW does nothing at all, which is demonstrated by
Memo1.Lines.Clear;
Memo1.Lines.Add(IntToHex(Get8087CW, 4)); // 1372
Set8087CW( Default8087CW or $08);
Memo1.Lines.Add(IntToHex(Get8087CW, 4)); // 137A
Reset8087CW;
Memo1.Lines.Add(IntToHex(Get8087CW, 4)); // 137A
Evidently a bug.
Now the first question - it was interesting debugging exercise.
The Default8087CW value of Delphi VCL application is changed from hex 1332 to 1372 by Windows.CreateWindowEx function, called from Classes.AllocateHWnd, called from TApplication.Create, called from initialization section of Controls.pas unit.
Have a look at CreateWindowEx code - it explains what happens. I don't really want to discuss it further - the FPU support in Delphi is too messy and buggy.

How to write Delphi compile-time functions

Delphi - can I write my own compile-time functions for const and var declarations, executable at compiler time.
Standard Delphi lib contain routines like Ord(), Chr(), Trunc(), Round(), High() etc, used for constant initialization.
Can I write my own, to execute routine at compile-time and use the result as constant?
You cannot write your own intrinsic functions. Because that requires compiler magic.
However there may be other options to achieve your goal.
Preprocessor
The only way is to use a preprocessor.
There are several: http://wiki.delphi-jedi.org/wiki/JEDI_Pre_Processor
The Delphi preprocessor
http://sourceforge.net/p/dpp32/wiki/Home/history
Andreas Hausladen has just open sourced his own work in this respect.
It's not really a preprocessor, but a language extender.
https://github.com/ahausladen/DLangExtensions
The problem with preprocessors is that it kills the link between the original (prior to pre-processing) source code and the source code that Delphi compiles.
This means that you will not have debug info for your original source.
(unless you rewrite the map file).
Inlining
Depending on what you want to do you can use inlining to achieve almost the same efficiency as an intrinsic function.
See: https://stackoverflow.com/a/6401833/650492
Construct your statements using intrinsic functions
If you have a code block consisting of instrinsic functions, the complete result will be evaluated at compile time, making the total construct work as if it was a intrinsic function.
Note the following (silly) example:
function FitsInRegister<T>: Boolean; inline;
begin
if GetTypeKind(T) in [tkString, tkUString] then result:= false
else
{$IFDEF CPU32BITS}
Result:= SizeOf(T) <= 4;
{$ELSEIF CPU64BITS}
Result:= SizeOf(T) <= 8;
{$ENDIF}
end;
Because it is inline and it only uses intrinsic functions (and compiler directives), the function will be resolved at compiletime to a constant and not generate any code.
Can I write my own, to execute routine at compile-time and use the result as constant?
No you cannot. These functions are built in to the compiler and if offers no extension mechanism to allow third parties to supply built in functions.

Disable exception handling and let windows catch it?

I want to disable the exception catching by Delphi and let Windows catch it - making it produce a window like "AppName crashed. Debug , Send", add this to Application events, create a memory dump and so on.
By default, Delphi catches all the exception in TApplication.Run procedure... How can I avoid that without modifying Forms.pas?
You could add an OnException handler that re-raised the exception:
class procedure TMainForm.OnException(Sender: TObject; E: Exception);
begin
raise Exception(AcquireExceptionObject);
end;
initialization
Application.OnException := TMainForm.OnException;
I'm not sure why you would want to do this at all though. It's more normal to use a tool like madExcept or EurekaLog to show an error dialog that yields much more helpful information than the system dialog.
You can set JITEnable to '1' or higher (default is '0'). With '1', non native exceptions, with higher than '1', all exceptions will be handled by JIT or WER (depending on the system).
This may not be what you want though. With this solution any qualifying exception will be passed to the OS, it doesn't matter if they're handled in code or not. Clarification (run outside the debugger):
procedure TForm1.Button1Click(Sender: TObject);
begin
raise EAccessViolation.Create('access denied');
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
try
PInteger(0)^ := 0;
except
end;
end;
initialization
JITEnable := 1;
The first example is a native exception, it will be handled by the application exception handling mechanism when JITEnable is 1. But the second example will trigger JIT/WER.
Add your own handler. Application.OnException is probably what you want. Better than leaving it up to windows as well, as you get different behaviours depending on the environment. For instance if windows error reporting is on, it will ask the user if they want to send an error report to MS.
Like Mr Heffernan I recommend you look at something like EurekaLog.
AS. I agree with voices above that this wish is rather strange.
I also agree that practically hooking in TApplication.OnException would probably be enough ("if it looks like a duck...")
However if you truly want to make RTL oblivious to exceptions, there are ways too.
Exception handlers are plugin to low-level RTL, just like heap management, etc.
You can look at KOL (Key Objects Library).
In Delphi 5 times i managed to make 2KB-size DLL.
That required absense of many usualyl taken "for granted" features. Exception were among them.
To enable Exceptions in KOL's system RTL replacement, you had to make some $DEFINE's, and then the code to add exceptions support to IDE was unlocked.
I believe you can still get that modularized RTL version and grep for that $IfDef and see which code is replaced with which.
I believe there is fair chance you can undo that and make Windows avoid calling Delphi RTL over Exceptions.
I don't remember details, but i believe Delphi RTL Exception handler is just registered in Windows core as a callback. And you probably can de-register it (register nil callback).
I believe you can find it in stock RTL, but KOL's modularised RTL would just make it easier to search.

Exceptions and DLL in Delphi

What is the right way to handle exceptions thrown from inside a DLL in Delphi?
Something like this
on E : ESomeException do ...
or
if (E is ESomeException) then ...
fails, because of the separate type registries of the DLL and the main application.
For pure DLL's exceptions are not allowed to cross the DLL boundary (like Deltics mentions) - no matter what language.
You get all sorts of trouble there, especially because you don't know which language, RTL, memory manager, etc, is on each side of the boundary.
So you are back to the classic error handling paradigm:
error codes (similar to HResult)
error messages (similar to GetLastError)
Instead of DLL's, you could use BPL packages (as Lars suggested): there you know that both sides will use the same RTL and memory manager.
Both packages and BPL usually give you a versioning nightmare anyway (too many degrees of freedom).
A more rigorous solution is to go for a monolithic executable; this solves both problems:
much easier versioning
guaranteed only one RTL and memory manager
--jeroen
PS: I've made this an additional answer because that allows for easier pasting of links.
The safest way is to not allow exceptions to "escape" from the DLL in the first place.
But if you have no control over the source of DLL and are therefore unable to ensure this, you can still test the exception class name:
if SameText(E.ClassName, 'ESomeException') then ...
If you use runtime packages (at least rtlxx.bpl) for both your application and your dll, then both have the same type and it will work. Of course this limits the use of your dll to Delphi/BCB only.
Another solution is not using exceptions at all like Deltics suggest. Return error codes.
Or use COM. Then you can have exceptions and not limit your dll to Delphi only.
Sometimes you do not have control over a DLL and cannot avoid having trouble with exceptions.
We, for instance, had a problem with a function in an external DLL that was blocked by AV software settings ("ransomware protection") leading to access violations in Delphi.
The following code is working for us:
var
O: TObject;
Msg: AnsiString; //type depending on DLL
begin
try
CallExternalDll(...);
except
//on E: Exception do might lead to access violations
//because the exception might not be of type Exception
O := ExceptObject;
//Depending on the DLL this or an other cast might
//or might not be necessary:
Msg := PAnsiChar(Exception(O).Message);
raise Exception.Create(Msg);
end;
end;
This workaround seems to do it for me:
function ExceptionMatch (Exc : Exception; ExcClass : TClass) : Boolean;
var
CurrClass : TClass;
begin
CurrClass := Exc.ClassType;
while (CurrClass <> nil) do
begin
if SameText (CurrClass.ClassName, ExcClass.ClassName) then
Exit (True);
CurrClass := CurrClass.ClassParent;
end;
Result := False;
end;
I'm prepared for you to destroy this :)
What is wrong with this approach? What is potentially dangerous?

Resources