{"id":99535,"date":"2018-08-21T07:00:00","date_gmt":"2018-08-21T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=99535"},"modified":"2019-03-13T00:38:32","modified_gmt":"2019-03-13T07:38:32","slug":"20180821-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20180821-00\/?p=99535","title":{"rendered":"The PowerPC 600 series, part 12: Leaf functions"},"content":{"rendered":"<p>On Windows NT for the PowerPC, there is a leaf function optimization available provided your function meets these criteria: <\/p>\n<ul>\n<li>    It calls no other functions. <\/li>\n<li>    It does not have an exception handler. <\/li>\n<li>    It does not need any stack space beyond     stack space used by actual inbound parameters,     the eight words of stack used as home space,&sup1;     and the 232-byte red zone. <\/li>\n<li>    It does not modify any nonvolatile registers. <\/li>\n<\/ul>\n<p>If all of these conditions are met, then the function does not need to declare any function unwind codes, and it does not need to set up a stack frame. It can reuse the stack frame of its caller. In order for the system to be able to unwind out of a lightweight leaf function, the leaf function must keep its return address in the <var>lr<\/var> register throughout the entire life of the function, and it cannot move the stack pointer. <\/p>\n<p>Conversely, if you fail to declare unwind codes for a function, then the system assumes that it is a lightweight leaf function. <\/p>\n<p>Here&#8217;s a sample function that is a candidate for lightweight leaf status: <\/p>\n<pre>\nwchar_t* SkipLeadingSpacesAndTabs(wchar_t* s)\n{\n    while (*s == L' ' || *s == L'\\t') s++;\n    return s;\n}\n<\/pre>\n<p>This is how the Microsoft compiler generated the code for it: <\/p>\n<pre>\nSkipLeadingSpacesAndTabs:\n    lhz     r4,(r3)     ; load wchar_t into r4\n    cmpwi   cr6,r4,0x20 ; Is it a space?\n    beq     cr6,loop    ; Y: skip it\n    cmpwi   cr7,r4,9    ; Is it a tab?\n    bne     cr7,break   ; N: done\nloop:\n    lhzu    r4,2(r3)    ; Skip over current character and load next one\n    cmpwi   cr6,r4,0x20 ; Is it a space?\n    beq     cr6,loop    ; Y: skip it\n    cmpwi   cr7,r4,9    ; Is it a tab?\n    beq     cr7,loop    ; Y: continue\nbreak:\n    blr                 ; Return to caller, result already in r3\n<\/pre>\n<p>For some reason, the Microsoft compiler likes to use <var>cr6<\/var> and <var>cr7<\/var> as the targets for its comparison instructions. It probably wants to stay far away from <var>cr0<\/var> and <var>cr1<\/var>, which are implicitly updated by some instructions. <\/p>\n<p>Notice that we used the <code>lhzu<\/code> instruction to advance the <var>r3<\/var> register and then fetch a halfword from it. This shows how the update version of a load instruction is handy for walking through an array. <\/p>\n<p>If we wanted to be clever, we could apply the following transformation. First, un-unroll the loop: <\/p>\n<pre>\nSkipLeadingSpacesAndTabs:\n    lhz     r4,(r3)     ; load wchar_t into r4\n    b       test\nloop:\n    lhzu    r4,2(r3)    ; Skip over current character and load next one\ntest:\n    cmpwi   cr6,r4,0x20 ; Is it a space?\n    beq     cr6,loop    ; Y: skip it\n    cmpwi   cr7,r4,9    ; Is it a tab?\n    beq     cr7,loop    ; Y: continue\nbreak:\n    blr                 ; Return to caller, result already in r3\n<\/pre>\n<p>This seems like a pessimization, since we introduced a branch. But now I can remove the branch by realizing that I can trick the first iteration&#8217;s <code>lhzu<\/code> to load the first halfword of the string rather than the second: Predecrement the value to counteract the preincrement! <\/p>\n<pre>\nSkipLeadingSpacesAndTabs:\n    subi    r3,r3,2     ; decrement to counteract the upcoming increment\nloop:\n    lhzu    r4,2(r3)    ; Skip over current character and load next one\n    cmpwi   cr6,r4,0x20 ; Is it a space?\n    beq     cr6,loop    ; Y: skip it\n    cmpwi   cr7,r4,9    ; Is it a tab?\n    beq     cr7,loop    ; Y: continue\nbreak:\n    blr                 ; Return to caller, result already in r3\n<\/pre>\n<p>Finally, I can combine the results of the two comparisons so there is only one branch that needs to be predicted:<\/p>\n<pre>\nSkipLeadingSpacesAndTabs:\n    subi    r3,r3,2     ; decrement to counteract the upcoming increment\nloop:\n    lhzu    r4,2(r3)    ; Skip over current character and load next one\n    cmpwi   cr6,r4,0x20 ; Is it a space?\n    cmpwi   cr7,r4,9    ; Is it a tab?\n    cror    4*cr7+eq,4*cr6+eq,4*cr7+eq ; Is it either?\n    beq     cr7,loop    ; Y: continue\n    blr                 ; Return to caller, result already in r3\n<\/pre>\n<p>I don&#8217;t know whether this performs better than the original code, but it is four instructions shorter, consumes one fewer branch prediction slot, and simply looks cooler. I win on style points, but I could very well lose on real-world performance. <\/p>\n<p><a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20180822-00\/?p=99545\">Next time<\/a>, we&#8217;ll look at common patterns for branches and calls. <\/p>\n<p>&sup1; As I noted earlier, you are allowed to use all of the home space even if your function doesn&#8217;t have that many parameters. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Making do with what you are given.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[2],"class_list":["post-99535","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>Making do with what you are given.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/99535","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=99535"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/99535\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=99535"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=99535"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=99535"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}