clang allows the following loop syntax:
for (...) #autorelease { ... }
while (...) #autorelease { ... }
do #autorelease { ... } while (...);
I haven't found any documentation on that syntax so far (Apple doesn't use this syntax in their guides, at least no in the guides introducing the #autorelease construct), but is it reasonable to assume that the three statement above are equivalent to the three statements below:
for (...) { #autorelease { ... } }
while (...) { #autorelease { ... } }
do { #autorelease { ... } } while (...);
Since that is what I would expect them to be (going by standard C syntax rules), yet I'm not entirely sure if that's really the case. It could also be some "special syntax", where the autorelease pool is not renewed for every loop iteration.
The reason that the first syntax example works is clear when you consider that any conditional statement can omit the { ... } block, resulting in only the following statement being executed.
For example:
if (something == YES)
NSLog(#"Something is yes");
is equivalent to
if (something == YES)
{
NSLog(#"Something is yes");
}
The #autoreleasepool { ... } block is simply the next statement following the conditional.
Personally I use the second syntax as it's less error-prone when making changes, and I find it easier to read. Imagine that when you add a statement between the conditional and the #autoreleasepool { ... } block, the result is considerably different from the original. See this naive example...
int i = 1;
while (i <= 10)
#autoreleasepool
{
NSLog(#"Iteration %d", i);
++i;
}
Will output "Iteration 1" through "Iteration 10". However:
int i = 1;
int total = 0;
while (i <= 10)
total += i;
#autoreleasepool
{
NSLog(#"Iteration %d", i);
++i;
}
Will actually cause an infinite loop because the ++i statement is never reached as it is syntactically equivalent to:
int i = 1;
int total = 0;
while (i <= 10)
{
total += i;
}
#autoreleasepool
{
NSLog(#"Iteration %d", i);
++i;
}
Both syntax are same
-(void)aFunc
{
int i=0;
for(;i<5;)
#autoreleasepool {
++i;
}
}
-(void)bFunc
{
int i=0;
for(;i<5;)
{
#autoreleasepool {
++i;
}
}
}
Assembly code
"-[AppDelegate aFunc]": ## #"\01-[AppDelegate aFunc]"
.cfi_startproc
Lfunc_begin0:
.loc 1 12 0 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:12:0
## BB#0:
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
subq $32, %rsp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
.loc 1 14 12 prologue_end ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:14:12
Ltmp5:
movl $0, -20(%rbp)
LBB0_1: ## =>This Inner Loop Header: Depth=1
.loc 1 15 5 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:15:5
Ltmp6:
cmpl $5, -20(%rbp)
jge LBB0_3
## BB#2: ## in Loop: Header=BB0_1 Depth=1
.loc 1 16 26 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:16:26
Ltmp7:
callq _objc_autoreleasePoolPush
.loc 1 17 13 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:17:13
movl -20(%rbp), %ecx
addl $1, %ecx
movl %ecx, -20(%rbp)
.loc 1 18 9 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:18:9
movq %rax, %rdi
callq _objc_autoreleasePoolPop
jmp LBB0_1
Ltmp8:
LBB0_3:
.loc 1 19 1 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:19:1
addq $32, %rsp
popq %rbp
ret
Ltmp9:
Lfunc_end0:
.file 2 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSObject.h"
.file 3 "/Users/Parag/Desktop/Test/Test/AppDelegate.h"
.cfi_endproc
.align 4, 0x90
"-[AppDelegate bFunc]": ## #"\01-[AppDelegate bFunc]"
.cfi_startproc
Lfunc_begin1:
.loc 1 20 0 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:20:0
## BB#0:
pushq %rbp
Ltmp12:
.cfi_def_cfa_offset 16
Ltmp13:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp14:
.cfi_def_cfa_register %rbp
subq $32, %rsp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
.loc 1 22 12 prologue_end ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:22:12
Ltmp15:
movl $0, -20(%rbp)
LBB1_1: ## =>This Inner Loop Header: Depth=1
.loc 1 23 5 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:23:5
Ltmp16:
cmpl $5, -20(%rbp)
jge LBB1_3
## BB#2: ## in Loop: Header=BB1_1 Depth=1
.loc 1 25 26 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:25:26
Ltmp17:
callq _objc_autoreleasePoolPush
.loc 1 26 14 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:26:14
movl -20(%rbp), %ecx
addl $1, %ecx
movl %ecx, -20(%rbp)
.loc 1 27 9 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:27:9
movq %rax, %rdi
callq _objc_autoreleasePoolPop
Ltmp18:
.loc 1 28 5 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:28:5
jmp LBB1_1
Ltmp19:
LBB1_3:
.loc 1 29 1 ## /Users/Parag/Desktop/Test/Test/AppDelegate.m:29:1
addq $32, %rsp
popq %rbp
ret
Ltmp20:
Lfunc_end1:
I have tried the following code:
#interface Foo : NSObject
#end
#implementation Foo
- (void) dealloc
{
NSLog(#"Deallocating %#.", self);
[super dealloc];
}
#end
for (;;) #autoreleasepool {
[[[Foo alloc] init] autorelease];
sleep(1);
}
The console starts to fill with the deallocated Foo instances, so the syntax appears to work as expected.
This is just the normal C syntax for blocks and statements. When if, else, for, while, etc. do not have braces, they take the following statement, which could be a compound statement.
For example, you can do:
for (...) if (...) { ... }
if (...) while (...) { ... }
and so on... #autoreleasepool blocks are no different.
Related
Does anyone know why I get Thread 5: EXC_BAD_ACCESS (code=1, address=0x0), when trying to create NSURLCredetials in the iOS specific code of Kotlin multi-platform project. I get the error when this code is executed:
val credentials = NSURLCredential.credentialWithUser(this.username, this.password, NSURLCredentialPersistence.NSURLCredentialPersistenceForSession)
And here is a bit more details about the error:
0x10d8f83cb <+123>: leaq 0x87f02e(%rip), %rdi ; __KonanTlsKey
0x10d8f83d2 <+130>: movl $0xe0, %esi
0x10d8f83d7 <+135>: callq 0x10d1662e0 ; LookupTLS
-> 0x10d8f83dc <+140>: leaq 0x87da7d(%rip), %rdi ; kobjref:platform.Foundation.NSURLCredentialPersistence.$OBJECT#internal
0x10d8f83e3 <+147>: leaq 0x6b8746(%rip), %rdx ; ktype:platform.Foundation.NSURLCredentialPersistence.$OBJECT
0x10d8f83ea <+154>: leaq -0x691(%rip), %rcx ; kfun:platform.Foundation.NSURLCredentialPersistence.$OBJECT.<init>()platform.Foundation.NSURLCredentialPersistence.$OBJECT at Foundation.kt
0x10d8f83f1 <+161>: movq %rax, %rsi
0x10d8f83f4 <+164>: movq -0x40(%rbp), %r8
I am currently working on famous System Programming problem, "Bomb lab" phase2. When I disassembled phase_2 with gdb, code looks like this...
Phase_2
0x00005555555555cb <+0>: endbr64
0x00005555555555cf <+4>: push %rbp
0x00005555555555d0 <+5>: push %rbx
0x00005555555555d1 <+6>: sub $0x28,%rsp
0x00005555555555d5 <+10>: mov %fs:0x28,%rax
0x00005555555555de <+19>: mov %rax,0x18(%rsp)
0x00005555555555e3 <+24>: xor %eax,%eax
0x00005555555555e5 <+26>: mov %rsp,%rsi
0x00005555555555e8 <+29>: callq 0x555555555bd5 <read_six_numbers>
0x00005555555555ed <+34>: cmpl $0x0,(%rsp)
0x00005555555555f1 <+38>: js 0x5555555555fd <phase_2+50>
0x00005555555555f3 <+40>: mov %rsp,%rbp
0x00005555555555f6 <+43>: mov $0x1,%ebx
0x00005555555555fb <+48>: jmp 0x555555555615 <phase_2+74>
0x00005555555555fd <+50>: callq 0x555555555ba9 <explode_bomb>
0x0000555555555602 <+55>: jmp 0x5555555555f3 <phase_2+40>
0x0000555555555604 <+57>: callq 0x555555555ba9 <explode_bomb>
0x0000555555555609 <+62>: add $0x1,%ebx
0x000055555555560c <+65>: add $0x4,%rbp
0x0000555555555610 <+69>: cmp $0x6,%ebx
0x0000555555555613 <+72>: je 0x555555555621 <phase_2+86>
0x0000555555555615 <+74>: mov %ebx,%eax
0x0000555555555617 <+76>: add 0x0(%rbp),%eax
0x000055555555561a <+79>: cmp %eax,0x4(%rbp)
0x000055555555561d <+82>: je 0x555555555609 <phase_2+62>
0x000055555555561f <+84>: jmp 0x555555555604 <phase_2+57>
0x0000555555555621 <+86>: mov 0x18(%rsp),%rax
0x0000555555555626 <+91>: xor %fs:0x28,%rax
0x000055555555562f <+100>: jne 0x555555555638 <phase_2+109>
0x0000555555555631 <+102>: add $0x28,%rsp
0x0000555555555635 <+106>: pop %rbx
0x0000555555555636 <+107>: pop %rbp
0x0000555555555637 <+108>: retq
0x0000555555555638 <+109>: callq 0x555555555220 <__stack_chk_fail#plt>
I guess that in line <+62>, %ebx means index, and increments the value until the value is 6 (by line <+69>). But I don't really understand lines such as
0x000055555555560c <+65>: add $0x4,%rbp
or
0x0000555555555615 <+74>: mov %ebx,%eax
0x0000555555555617 <+76>: add 0x0(%rbp),%eax
0x000055555555561a <+79>: cmp %eax,0x4(%rbp)
I guess it is probably related to features of sequence that I have to find, but I don't understand why values such as 0x4(%rbp)is compared with %eax etc... Can someone explain?
My app is crashing any time I present a UIActivityViewController, only on iOS9.
I've isolated the code down to this: (Cleared everything from the AppDelegate minus setting my test view controller as the root and only VC).
In the test view controller on button tap:
(I made the activityVC an instance property to check if there was a deallocation issue because other debugging efforts suggested that the activityVC might get dealloced during presentation)
NSString *string = #"PLS WORK";
self.activityViewController =
[[UIActivityViewController alloc] initWithActivityItems:#[string]
applicationActivities:nil];
[self presentViewController:self.activityViewController
animated:YES
completion:nil];
yields
2015-09-21 11:41:55.889 App[21548:606985] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIView
_fromWindowOrientation]: unrecognized selector sent to instance 0x7f9168f25760'
*** First throw call stack: ( 0 CoreFoundation 0x000000010c68df65 __exceptionPreprocess + 165 1 libobjc.A.dylib 0x000000010be08deb objc_exception_throw + 48 2 CoreFoundation 0x000000010c69658d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205 3 CoreFoundation 0x000000010c5e3f7a
___forwarding___ + 970 4 CoreFoundation 0x000000010c5e3b28 _CF_forwarding_prep_0 + 120 5 UIKit 0x000000010b104aa0 -[_UIViewControllerTransitionContext
_affineTransform] + 132 6 UIKit 0x000000010b104c77 -[_UIViewControllerTransitionContext targetTransform] + 29 7 UIKit 0x000000010b10895f -[_UIViewControllerTransitionCoordinator targetTransform] + 40 8 UIKit 0x000000010b109483 _UIViewControllerTransitionCoordinatorIsRotating + 35 9 UIKit 0x000000010a8fbcba
-[UIViewController viewWillTransitionToSize:withTransitionCoordinator:] + 78 10 UIKit 0x000000010aa5e68d __79-[UIAlertController
_contentViewControllerWillTransitionToSize:withAnimations:]_block_invoke591
+ 154 11 UIKit 0x000000010a7faeec +[UIView(Animation) performWithoutAnimation:] + 65 12 UIKit 0x000000010b03b693 -[_UIAnimationCoordinator animate] + 128 13 UIKit 0x000000010aa5e54d -[UIAlertController
_contentViewControllerWillTransitionToSize:withAnimations:] + 1071 14 UIKit 0x000000010b0d2910
-[_UIAlertControllerView _sizeOfContentViewControllerChanged] + 1009 15 UIKit 0x000000010aa5afe8
-[UIAlertController viewDidLoad] + 659 16 UIKit 0x000000010a8e0931 -[UIViewController loadViewIfRequired] + 1344 17 UIKit 0x000000010a8e0c7d
-[UIViewController view] + 27 18 UIKit 0x000000010b0292ab -[UIActivityViewController viewDidLoad] + 1266 19 UIKit 0x000000010a8e0931
-[UIViewController loadViewIfRequired] + 1344 20 UIKit 0x000000010a8e0c7d -[UIViewController view] + 27 21 UIKit 0x000000010a8fa738 -[UIViewController _setPresentationController:] + 100 22 UIKit 0x000000010a8f1cf4
-[UIViewController _presentViewController:modalSourceViewController:presentationController:animationController:interactionController:completion:]
+ 1437 23 UIKit 0x000000010a8f3970 -[UIViewController _presentViewController:withAnimationController:completion:] + 4870 24 UIKit 0x000000010a8f6878
-[UIViewController _performCoordinatedPresentOrDismiss:animated:] + 489 25 UIKit 0x000000010a8f6387
-[UIViewController presentViewController:animated:completion:] + 179 26 App 0x0000000107607734
-[YYWTFViewController wtfButtonPressed:] + 644 27 UIKit 0x000000010a7571fa -[UIApplication sendAction:to:from:forEvent:] + 92 28 UIKit 0x000000010a8bb504
-[UIControl sendAction:to:forEvent:] + 67 29 UIKit 0x000000010a8bb7d0 -[UIControl _sendActionsForEvents:withEvent:] + 311 30 UIKit 0x000000010a8ba906
-[UIControl touchesEnded:withEvent:] + 601 31 UIKit 0x000000010a7c1aa3 -[UIWindow _sendTouchesForEvent:] + 835 32 UIKit 0x000000010a7c2691 -[UIWindow sendEvent:] + 865 33 UIKit 0x000000010a774752 -[UIApplication sendEvent:] + 263 34 UIKit 0x000000010a74ffcc _UIApplicationHandleEventQueue + 6693 35 CoreFoundation 0x000000010c5ba0a1
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17 36 CoreFoundation 0x000000010c5affcc
__CFRunLoopDoSources0 + 556 37 CoreFoundation 0x000000010c5af483 __CFRunLoopRun + 867 38 CoreFoundation 0x000000010c5aee98 CFRunLoopRunSpecific + 488 39 GraphicsServices 0x000000010f0d9ad2 GSEventRunModal + 161 40 UIKit 0x000000010a755676 UIApplicationMain + 171 41 App 0x000000010766101f main + 111 42 libdyld.dylib 0x000000010d98792d start + 1 )
Assembler crash log:
0x10a7555cb <+0>: pushq %rbp
0x10a7555cc <+1>: movq %rsp, %rbp
0x10a7555cf <+4>: pushq %r15
0x10a7555d1 <+6>: pushq %r14
0x10a7555d3 <+8>: pushq %r13
0x10a7555d5 <+10>: pushq %r12
0x10a7555d7 <+12>: pushq %rbx
0x10a7555d8 <+13>: pushq %rax
0x10a7555d9 <+14>: movq %rcx, %rbx
0x10a7555dc <+17>: movq %rsi, %r15
0x10a7555df <+20>: movl %edi, %r12d
0x10a7555e2 <+23>: movq 0xcc7ba7(%rip), %r13 ; (void *)0x000000010be1bd00: objc_retain
0x10a7555e9 <+30>: movq %rdx, %rdi
0x10a7555ec <+33>: callq *%r13
0x10a7555ef <+36>: movq %rax, %r14
0x10a7555f2 <+39>: movq %rbx, %rdi
0x10a7555f5 <+42>: callq *%r13
0x10a7555f8 <+45>: movq %rax, %r13
0x10a7555fb <+48>: leaq 0xcc5aea(%rip), %rbx ; _UIApplicationLinkedOnVersion
0x10a755602 <+55>: movl (%rbx), %eax
0x10a755604 <+57>: testl %eax, %eax
0x10a755606 <+59>: jne 0x10a755627 ; <+92>
0x10a755608 <+61>: cmpq $-0x1, 0xcc18a0(%rip) ; WebKitSetIsClassic + 7
0x10a755610 <+69>: je 0x10a755651 ; <+134>
0x10a755612 <+71>: leaq 0xcc1897(%rip), %rdi ; _UIApplicationLinkedOnVersionOnce
0x10a755619 <+78>: leaq 0xccaeb0(%rip), %rsi ; __block_literal_global1457
0x10a755620 <+85>: callq 0x10b1a3f5c ; symbol stub for: dispatch_once
0x10a755625 <+90>: movl (%rbx), %eax
0x10a755627 <+92>: cmpl $0x20100, %eax
0x10a75562c <+97>: jb 0x10a755651 ; <+134>
0x10a75562e <+99>: callq 0x10b1a3746 ; symbol stub for: objc_autoreleasePoolPush
0x10a755633 <+104>: movq %rax, %rbx
0x10a755636 <+107>: movl %r12d, %edi
0x10a755639 <+110>: movq %r15, %rsi
0x10a75563c <+113>: movq %r14, %rdx
0x10a75563f <+116>: movq %r13, %rcx
0x10a755642 <+119>: callq 0x10a755698 ; _UIApplicationMainPreparations
0x10a755647 <+124>: movq %rbx, %rdi
0x10a75564a <+127>: callq 0x10b1a3740 ; symbol stub for: objc_autoreleasePoolPop
0x10a75564f <+132>: jmp 0x10a755662 ; <+151>
0x10a755651 <+134>: movl %r12d, %edi
0x10a755654 <+137>: movq %r15, %rsi
0x10a755657 <+140>: movq %r14, %rdx
0x10a75565a <+143>: movq %r13, %rcx
0x10a75565d <+146>: callq 0x10a755698 ; _UIApplicationMainPreparations
0x10a755662 <+151>: movq 0xcc59df(%rip), %rdi ; UIApp
0x10a755669 <+158>: movq 0xc4a3c8(%rip), %rsi ; "_run"
0x10a755670 <+165>: callq *0xcc7b0a(%rip) ; (void *)0x000000010be1e800: objc_msgSend
0x10a755676 <+171>: movq 0xcc7b0b(%rip), %rbx ; (void *)0x000000010be1bd70: objc_release <--- DEAD
What is odd is that creating a new project targeted for iOS9 with the exact same code does not crash. I don't know how I can isolate this issue in my project any further. I'd appreciate any hints or debugging suggestions!
If you have a category on UIViewController with a property named window, this is the result. This only happens since the release of iOS 9.
With the release of iOS 9, Apple introduced a property named window on a UIViewController -- Check the use of your variable names.
Should I avoid using the 'insertvalue' instruction combined with load and store when I emit LLVM code?
I always get bad optimized native code when I use it. Look at the following example:
; ModuleID = 'mod'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-pc-linux-gnu"
%A = type { i64, i64, i64, i64, i64, i64, i64, i64 }
#aa = external global %A*
define void #func() {
entry:
%a1 = load %A** #aa
%a2 = load %A* %a1
%a3 = insertvalue %A %a2, i64 3, 3
store %A %a3, %A* %a1
ret void
}
When I run "llc -o - -O3 mod.ll", I get this horrible code:
func: # #func
.Ltmp0:
.cfi_startproc
# BB#0: # %entry
movq aa(%rip), %rax
movq (%rax), %r8
movq 8(%rax), %r9
movq 16(%rax), %r10
movq 32(%rax), %rdi
movq 40(%rax), %rcx
movq 48(%rax), %rdx
movq 56(%rax), %rsi
movq %rsi, 56(%rax)
movq %rdx, 48(%rax)
movq %rcx, 40(%rax)
movq %rdi, 32(%rax)
movq %r10, 16(%rax)
movq %r9, 8(%rax)
movq %r8, (%rax)
movq $3, 24(%rax)
ret
But what I would like to see is this:
func: # #func
.Ltmp0:
.cfi_startproc
# BB#0: # %entry
movq aa(%rip), %rax
movq $3, 24(%rax)
ret
Of course I can use getelementptr or something, but sometimes it is easier to generate insertvalue and extractvalue instructions, and I want these to be optimized...
I think it would be quite easy for the codegen to see that things like these are bad:
movq 56(%rax), %rsi
movq %rsi, 56(%rax)
First, note that llc does not do any IR-level optimizations. So, you should run opt to run the set of IR-level optimizers.
However, opt does not help in this. I'd expect that standard IR-level optimizers canonicalize the stuff into gep somehow.
Please file a LLVM PR, this looks like a missed optimization!
If I write printf("%%%s","hello"), how is it interpreted as by the compiler? Enlighten me, someone.
The compiler simply interprets this as calling printf with two strings as arguments (but see Zack's comment).
This happens at compile time (i.e. the compiler does this):
The strings ("%%%s" and "hello") are copied directly into the executable, the compiler leaves them as-is.
This happens at runtime (i.e. the C standard library does this when the app is running):
printf stand for 'print formatted'. When this function is called, it needs at least one argument. The first argument is the format. The next arguments are "arguments" to this format. They are formatted as specified in the first argument.
About optimization
I have written an example and ran Clang/LLVM with -S:
$ emacs printftest.c
$ clang printftest.c -S -o printftest_unopt.s # not optimized
$ clang printftest.c -S -O -o printftest_opt.s # optimized: -O flag
C code
#include <stdio.h>
int main() {
printf("%%%s", "hello");
return 0;
}
printftest_unopt.s (not optimized)
; ...
_main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $0, %eax
movl $0, -4(%rbp)
movl %eax, -8(%rbp)
xorb %al, %al
leaq L_.str(%rip), %rdi
leaq L_.str1(%rip), %rsi
callq _printf ; printf called here <----------------
movl %eax, -12(%rbp)
movl -8(%rbp), %eax
addq $16, %rsp
popq %rbp
ret
.section __TEXT,__cstring,cstring_literals
L_.str:
.asciz "%%%s"
L_.str1:
.asciz "hello"
; ...
printftest_opt.s (optimized)
; ...
_main:
pushq %rbp
movq %rsp, %rbp
leaq L_.str(%rip), %rdi
leaq L_.str1(%rip), %rsi
xorb %al, %al
callq _printf ; printf called here <----------------
xorl %eax, %eax
popq %rbp
ret
.section __TEXT,__cstring,cstring_literals
L_.str:
.asciz "%%%s"
L_.str1:
.asciz "hello"
; ...
Conclusion
As you can see (in the __TEXT,__cstring,cstring_literals section and the callq to printf), LLVM (a very, very good compiler) does not optimize printf("%%%s", "hello");. :)
"%%" means to print an actual "%" character; %s means to print a string from the argument list. So you'll see "%hello".
%% will print a literal '%' character.