<html><head><meta http-equiv="Content-Type" content="text/html charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Feb 29, 2016, at 8:59 AM, Teresa Johnson <<a href="mailto:tejohnson@google.com" class="">tejohnson@google.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><br class="Apple-interchange-newline"><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class="gmail_quote" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;">On Mon, Feb 29, 2016 at 8:45 AM, Mehdi Amini<span class="Apple-converted-space"> </span><span dir="ltr" class=""><<a href="mailto:mehdi.amini@apple.com" target="_blank" class="">mehdi.amini@apple.com</a>></span><span class="Apple-converted-space"> </span>wrote:<br class=""><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"><div dir="auto" class=""><div class=""><br class=""><br class="">Sent from my iPhone</div><span class=""><div class=""><br class="">On Feb 29, 2016, at 8:18 AM, Teresa Johnson <<a href="mailto:tejohnson@google.com" target="_blank" class="">tejohnson@google.com</a>> wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><br class=""><div class="gmail_extra"><br class=""><div class="gmail_quote">On Fri, Feb 26, 2016 at 1:54 PM, Teresa Johnson<span class="Apple-converted-space"> </span><span dir="ltr" class=""><<a href="mailto:tejohnson@google.com" target="_blank" class="">tejohnson@google.com</a>></span><span class="Apple-converted-space"> </span>wrote:<br class=""><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"><div dir="ltr" class=""><br class=""><div class="gmail_extra"><br class=""><div class="gmail_quote"><span class="">On Fri, Feb 26, 2016 at 11:18 AM, Xinliang David Li<span class="Apple-converted-space"> </span><span dir="ltr" class=""><<a href="mailto:davidxl@google.com" target="_blank" class="">davidxl@google.com</a>></span><span class="Apple-converted-space"> </span>wrote:<br class=""><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"><div dir="ltr" class="">In previous discussions with Teresa, we mentioned recording static callsite info in the summary -- this can drive more intelligent inline decisions to enable more linker GC.</div></blockquote><div class=""><br class=""></div></span><div class="">Yes, see WIP patch D17656.</div><span class=""><div class=""><br class=""></div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"><div dir="ltr" class=""><div class=""><br class=""></div><div class="">Regarding global variable analysis -- I think there is a way for thinLTO to work nicely in whole program mode.<span class="Apple-converted-space"> </span></div><div class=""> The variable reference graph need edge that records access types (read, write, address taken). The plugin step will merge the information (with real linker feedback) ..</div></div></blockquote><div class=""><br class=""></div></span><div class="">Based on various potential use cases mentioned here and in D17656 and discussions with Mehdi, I am going to try expanding this to be a full-fledged reference graph. It may not include the access types initially. Need to think about how best to represent the variable and function reference information in the bitcode and the index...</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">I collected some stats about the number of GV references in functions across SPEC cpu2006, both as callees (expanded the current logic to identify invokes as well), as well as other GV references. When each function is written into bitcode, where I am scanning for current function summary info, I iterate over all instruction operands(), and look for any that are GlobalValues. Then I check if the function has a callsite and depending on whether the GV is the callee or not, increment the appropriate counter. I think that should get all of the GV references?</div><div class=""><br class=""></div><div class="">Across all benchmarks and overall, most functions contain at least one call (74% overall). The situation for non-callee GV refs is a bit more mixed from benchmark to benchmark, but overall across all benchmarks most (77%) have none.</div><div class=""><br class=""></div><div class="">Given that, here is some thinking about the best bitcode representation:</div><div class=""><br class=""></div><div class="">For the current patch, each call is at least a tuple (callee value id, static #callsites) or a triple including the profile count with PGO. I suspect we don't care as much about the static # references or the profile count of those references for non-callsite GV references. And as David suggested, we may want to include other stats eventually for these normal references (e.g. read, write, address taken). So I don't think we want to use the same tuple or triple as we are using for the calls.</div><div class=""><br class=""></div><div class="">Additionally, each bitcode record may only contain a single array, at the end. So we will need to encode this single array with info in the record on how to decode it. E.g. in the current patch I am using different abbrev ids for the no call, calls without pgo and calls with pgo cases. But as Mehdi noted, as we add additional cases this could result in a combinatoric explosion of abbrev ids. And in any case, we would need to note in the record how many entries are for callee references vs non-callee GV references. Since most functions have at least one call and most have no other GV references, we could optimize this a bit by including a VBR to indicate the number of non-callee references (in most cases this will be a single VBR chunk containing the value 0), and deducing the number of callee references from the size of the record minus the number of non-callee references. (In fact, I could get rid of the NOCALLS abbrev id variants in the current patch using this approach to deduce whether there are any calls encoded in the record.)  However, the disadvantage of this approach is that it requires an additional VBR per summary record.</div><div class=""><br class=""></div><div class="">Another approach would be to include the references in a separate subsequent summary section record. Since most summaries contain calls, we may want to continue to include the calls in the main summary record, or could also encode those in a separate record type for consistency. The issue then becomes how to correlate those records with the main corresponding summary record. For per-module summaries, the function summary contains the value id, but including that in the reference records could require a bigger size overhead. Additionally, the combined summaries don't include value ids, they are associated with the corresponding VST entry by including the bitcode offset of the summary record in the combined VST. However, I think we can handle this by simply requiring that the reference records immediately follow the corresponding summary record. There is already precedence in the bitcode format for the order of the record being important for correlation - specifically the order of the MODULE_CODE_GLOBALVAR and MODULE_CODE_FUNCTION records implicitly encodes their value ids (for correlation with VST entries).</div><div class=""><br class=""></div><div class="">Given the above, I would suggest that we have 4 abbrev ids for the function summary section. Using the PERMODULE case as an example, this would be:</div><div class=""><br class=""></div><div class="">FS_PERMODULE_ENTRY: [valueid, linkage, instcount]</div><div class="">FS_PERMODULE_CALLS: [n x (calleevalueid, callsitecount)]</div><div class="">FS_PERMODULE_CALLS_PROFILE: [n x (calleevalueid, callsitecount, profilecount)]<br class=""></div><div class="">FS_PERMODULE_REFS: [n x refvalueid]<br class=""></div><div class=""><br class=""></div><div class="">where only one of FS_PERMODULE_CALLS/FS_PERMODULE_CALLS_PROFILE would be specified, and the ordering for each function would have to be ENTRY then the CALLS and REFS records, with no intervening summary records from other functions.<span class="Apple-converted-space"> </span></div></div></div></div></div></blockquote><div class=""><br class=""></div></span><div class="">Isn't it conceptually having a block per function?</div><div class="">This is something it mentioned when we talked about the combinatorial issue with codes.</div><div class="">What's the drawback of using blocks?</div></div></blockquote><div class=""><br class=""></div><div class="">Just to clarify, I think you are suggesting something like the following organization:</div><div class=""><br class=""></div><div class="">   <FUNCTION_SUMMARY_BLOCK ...</div><div class="">      <SUMMARY_BLOCK ...><br class=""></div><div class="">         <FS_PERMODULE_ENTRY: [valueid, linkage, instcount]></div><div class="">         <FS_PERMODULE_CALLS: [n x (calleevalueid, callsitecount)]></div><div class="">         <FS_PERMODULE_REFS: [n x refvalueid]></div><div class="">      </SUMMARY_BLOCK ...><br class=""></div><div class="">      ...</div><div class="">   </FUNCTION_SUMMARY_BLOCK><br class=""></div><div class=""><br class=""></div><div class="">With one block per function?</div></div></div></blockquote><div><br class=""></div><div>Yes.</div><br class=""><blockquote type="cite" class=""><div class=""><div class="gmail_quote" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><div class="">Does that have any advantage over the scheme where we don't have the additional level of block and the associated records must be adjacent?</div></div></div></blockquote><div><br class=""></div><div>It seems more "structured" and representing more accurately the data we're storing. </div><div>But I don't know all the trade-off associated with it? (for example is it possible for the record in a SUMMARY_BLOCK to use abbrev id from the FUNCTION_SUMMARY_BLOCK?)</div><div><br class=""></div><div>I don't have a strong opinion on this, just wondering.</div><div><br class=""></div><div>-- </div><div>Mehdi</div><div><br class=""></div><div><br class=""></div><blockquote type="cite" class=""><div class=""><div class="gmail_quote" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"><div dir="auto" class=""><div class=""><div class="h5"><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_extra"><div class="gmail_quote"><div class="">Also, the REFS case could eventually be extended to include other information for each refvalueid.</div><div class=""><br class=""></div><div class="">Like I said earlier, though, since most functions have calls, we could reduce the required records by including the call array with the other summary info (e.g. combine the CALLS with the ENTRY as in the current patch). That would save a bit of space (one less abbrev id to define per bitcode file, and 3 less bits per function by avoiding specifying the extra abbrev id).</div><div class=""><br class=""></div><div class="">Thoughts?</div><div class=""><br class=""></div><div class="">Thanks,</div><div class="">Teresa</div><div class=""><br class=""></div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"><div dir="ltr" class=""><div class="gmail_extra"><div class="gmail_quote"><span class=""><font color="#888888" class=""><div class=""><br class=""></div><div class="">Teresa</div></font></span><div class=""><div class=""><div class=""> </div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"><div dir="ltr" class=""><span class=""><font color="#888888" class=""><div class=""><br class=""></div><div class="">David</div></font></span></div><div class=""><div class=""><div class="gmail_extra"><br class=""><div class="gmail_quote">On Fri, Feb 26, 2016 at 11:06 AM, Mehdi Amini<span class="Apple-converted-space"> </span><span dir="ltr" class=""><<a href="mailto:mehdi.amini@apple.com" target="_blank" class="">mehdi.amini@apple.com</a>></span><span class="Apple-converted-space"> </span>wrote:<br class=""><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"><div style="word-wrap: break-word;" class=""><br class=""><div class=""><span class=""><blockquote type="cite" class=""><div class="">On Feb 26, 2016, at 10:58 AM, Xinliang David Li <<a href="mailto:davidxl@google.com" target="_blank" class="">davidxl@google.com</a>> wrote:</div><br class=""><div class=""><br class=""><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px;" class=""><div class="gmail_quote" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px;">On Fri, Feb 26, 2016 at 10:53 AM, Mehdi Amini<span class=""> </span><span dir="ltr" class=""><<a href="mailto:mehdi.amini@apple.com" target="_blank" class="">mehdi.amini@apple.com</a>></span><span class=""> </span>wrote:<br class=""><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"><span class=""><br class="">> On Feb 26, 2016, at 10:38 AM, Teresa Johnson <<a href="mailto:tejohnson@google.com" target="_blank" class="">tejohnson@google.com</a>> wrote:<br class="">><br class="">> tejohnson added a comment.<br class="">><br class="">> In<span class=""> </span><a href="http://reviews.llvm.org/D17212#362864" rel="noreferrer" target="_blank" class="">http://reviews.llvm.org/D17212#362864</a>, @joker.eph wrote:<br class="">><br class="">>> How could we integrate accesses to global variable as part of this?<br class="">>> It turns out that to be able to benefit from the linker information on what symbol is exported during the import, this is a must.<br class="">><br class="">><br class="">> Well without it you still can see which function symbols will be exported, just not the variables, so you are running with less info and I guess need to assume that all static variables will be exposed and promote them.<br class=""><br class=""></span>The scheme I am currently setting up is:<br class=""><br class="">1) The linker gives us the list of symbols that need to be "preserved" (can't internalize)<br class="">2) Link the combined index<br class="">3) Compute the import list for every module *by just looking at the profile*<br class="">4) Do the promotion<br class=""><br class="">There is absolutely no assumption for the promotion (last step): you exactly know what will be imported by *every module*, and you can promote the optimal minimal amount of symbols.<br class=""><br class="">All of that is good and should work with your call-graph patch "as is".<br class=""><br class="">I'm looking to go two steps further during stage 3:<br class=""><br class="">1) I want to drive the importing heuristic cost to actually take into account the need for promotion.<br class="">I'll start some test on the extreme case by *forbiding* any promotion, i.e. if a function references an internal function or global, then it can't be imported in any other module. On the long term it may be interesting to include this in the importing threshold.<br class="">This can be implemented with a flag or an int in the summary "numberOfInternalGlobalsReferenced", but will not be enough for step 2 (below).<br class=""><br class=""></blockquote><div class=""><br class=""></div><div class="">Interesting. What is the motivation for this type of tuning? Does is try to preserve more variable analysis (e.g, aliasing) in backend compilation?</div></div></div></blockquote><div class=""><br class=""></div></span><div class="">Yes! LLVM optimization bails terribly with global variable, but likes a lot "internal" variables. On some other (platform dependent) aspect, accessing non-internal globals requires going through the GOT which is not the case with internal. Internal globals can be totally eliminated or turned into allocas, which in turn enables more other optimization. </div><div class="">In the same way, in many aspects, the optimizer benefits from internal functions. </div><div class=""><br class=""></div><div class="">Some things can be recovered with more effort (for instance in the linker plugin for ThinLTO we're running :   internalize + global opt + global DCE *before* doing any promotion). But we will fight assumptions in the optimizer for a long time.</div><div class=""><br class=""></div><div class="">All of this is driven by our current performance tuning of course. </div><div class="">We are seeing Full-LTO generating a binary almost two times smaller on test-suite/trunk/SingleSource/Benchmarks/Adobe-C++/loop_unroll.cpp while ThinLTO is on the same level as the regular O3.</div><div class="">(this is a single source benchmark, I wonder if the inliner heuristic wouldn't need some tuning for O3 in general).</div><div class=""><br class=""></div>If you have any other idea or input :)</div><span class=""><font color="#888888" class=""><div class=""><br class=""></div><div class="">-- </div><div class="">Mehdi</div></font></span><span class=""><div class=""><br class=""> <br class=""><blockquote type="cite" class=""><div class="gmail_quote" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px;"><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;">2) I want to benefit from the linker information from stage 1 to internalize symbols.<br class="">It means that the information about the fact that a function is referencing an internal global *can't be in the summary* because the front-end does not know that the global will be internalized.<br class="">This can be implemented by not having a "call graph" but a "reference graph" (not sure on the terminology): i.e. edges would be there for any uses of a symbol to another one and not only calls.<br class=""></blockquote><div class=""><br class=""></div><div class="">Reference graph is what GCC uses :)</div><div class=""><br class=""></div><div class=""> </div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"><span class=""><br class=""><br class=""><br class="">> To refine that behavior for variables, yes, we'd need additional info in the summary.<br class="">><br class="">> (For davidxl or anyone else who didn't see the IRC conversation, Mehdi is looking at doing pure summary-based importing decisions in the linker step, then giving this info to the ThinLTO backends to avoid promotion of local values that aren't exported. For a distributed build if we wanted to do it this way the importing decisions would all be made in the plugin step, then would need to be serialized out for the distributed backends to check.)<br class="">><br class="">> Two possibilities depending on the fidelity of the info you think you need:<br class="">><br class="">> 1. One possibility is to just put a flag in the function summary if it accesses *any* local variables, and adjust the importing threshold accordingly. Then in the ThinLTO backend for the exporting module you need to check which of your own functions are on the import list, and which local variables they access, and promote accordingly.<br class="">><br class="">> 2. If it will be really beneficial to note exactly which local variables are accessed by which function, we'll need to broaden the edges list to include accesses to variables (I assume you only care about local variables here). E.g. the per-module summary edge list for a function would need to include value ids for any local variables referenced by that function (not sure that the other parts of the triple, the static and profile counts, are needed for that). Then in the combined VST we need to include entries for GUIDs of things that don't have a function summary, but are referenced by these edges. When accessing a function summary edge list for a candidate function to import, you could then see the GUID of any local variables accessed. You wouldn't know them by name, but if for example you wanted a heuristic like "if >=N hot import candidate functions from module M access a local variable with GUID G, go ahead and import those and let G be promoted by the backend (which like in approach #1 needs to check which local variables are accessed by any functions on an import list)".<br class="">><br class="">> Obviously 1) is easier and cheaper space-wise. What are your thoughts?<br class=""><br class=""><br class=""></span>So 1) is cheaper, but 2) a lot more powerful as explained above :)<br class=""></blockquote><div class=""><br class=""></div><div class="">yes -- we may find other good uses of it.</div><div class=""><br class=""></div><div class="">David</div><div class=""> </div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"><span class=""><font color="#888888" class=""><br class=""><br class="">--<br class="">Mehdi</font></span></blockquote></div></blockquote></div><br class=""></span></div></blockquote></div><br class=""></div></div></div></blockquote></div></div></div><br class=""><br clear="all" class=""><span class=""><div class=""><br class=""></div>--<span class="Apple-converted-space"> </span><br class=""><div class=""><span style="font-family: Times; font-size: inherit;" class=""><table cellspacing="0" cellpadding="0" class=""><tbody class=""><tr style="color: rgb(85, 85, 85); font-family: sans-serif; font-size: small;" class=""><td nowrap="" style="border-top-style: solid; border-top-color: rgb(213, 15, 37); border-top-width: 2px;" class="">Teresa Johnson |</td><td nowrap="" style="border-top-style: solid; border-top-color: rgb(51, 105, 232); border-top-width: 2px;" class=""> Software Engineer |</td><td nowrap="" style="border-top-style: solid; border-top-color: rgb(0, 153, 57); border-top-width: 2px;" class=""> <a href="mailto:tejohnson@google.com" target="_blank" class="">tejohnson@google.com</a> |</td><td nowrap="" style="border-top-style: solid; border-top-color: rgb(238, 178, 17); border-top-width: 2px;" class=""> <a href="tel:408-460-2413" value="+14084602413" target="_blank" class="">408-460-2413</a></td></tr></tbody></table></span></div></span></div></div></blockquote></div><br class=""><br clear="all" class=""><div class=""><br class=""></div>--<span class="Apple-converted-space"> </span><br class=""><div class=""><span style="font-family: Times; font-size: inherit;" class=""><table cellspacing="0" cellpadding="0" class=""><tbody class=""><tr style="color: rgb(85, 85, 85); font-family: sans-serif; font-size: small;" class=""><td nowrap="" style="border-top-style: solid; border-top-color: rgb(213, 15, 37); border-top-width: 2px;" class="">Teresa Johnson |</td><td nowrap="" style="border-top-style: solid; border-top-color: rgb(51, 105, 232); border-top-width: 2px;" class=""> Software Engineer |</td><td nowrap="" style="border-top-style: solid; border-top-color: rgb(0, 153, 57); border-top-width: 2px;" class=""> <a href="mailto:tejohnson@google.com" target="_blank" class="">tejohnson@google.com</a> |</td><td nowrap="" style="border-top-style: solid; border-top-color: rgb(238, 178, 17); border-top-width: 2px;" class=""> <a href="tel:408-460-2413" value="+14084602413" target="_blank" class="">408-460-2413</a></td></tr></tbody></table></span></div></div></div></div></blockquote></div></div></div></blockquote></div><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br clear="all" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class=""></div><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">--<span class="Apple-converted-space"> </span></span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class="gmail_signature" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><span style="font-family: Times; font-size: inherit;" class=""><table cellspacing="0" cellpadding="0" class=""><tbody class=""><tr style="color: rgb(85, 85, 85); font-family: sans-serif; font-size: small;" class=""><td nowrap="" style="border-top-style: solid; border-top-color: rgb(213, 15, 37); border-top-width: 2px;" class="">Teresa Johnson |</td><td nowrap="" style="border-top-style: solid; border-top-color: rgb(51, 105, 232); border-top-width: 2px;" class=""> Software Engineer |</td><td nowrap="" style="border-top-style: solid; border-top-color: rgb(0, 153, 57); border-top-width: 2px;" class=""> <a href="mailto:tejohnson@google.com" target="_blank" class="">tejohnson@google.com</a> |</td><td nowrap="" style="border-top-style: solid; border-top-color: rgb(238, 178, 17); border-top-width: 2px;" class=""> 408-460-2413</td></tr></tbody></table></span></div></div></blockquote></div><br class=""></body></html>