# R8 Optimization: Null Data Flow Analysis (Part 1)

DevFeed: [R8 Optimization: Null Data Flow Analysis (Part 1)](<https://devfeed.tech/articles/r8-optimization-null-data-flow-analysis-part-1-20961.md>)

Original publisher: [Read original article](<https://jakewharton.com/r8-optimization-null-data-flow-analysis-part-1/>)

Published: 2018-12-18T00:00:00Z

Content type: article

Language: en

Sources: [Jake Wharton](<https://devfeed.tech/sources/jake-wharton.md>)

Topics: [R8](<https://devfeed.tech/topics/r8.md>), [Optimization](<https://devfeed.tech/topics/optimization.md>), [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [Android](<https://devfeed.tech/topics/android.md>), [Compiler](<https://devfeed.tech/topics/compiler.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [analysis](<https://devfeed.tech/tags/analysis.md>), [android](<https://devfeed.tech/tags/android.md>), [code](<https://devfeed.tech/tags/code.md>), [compiler](<https://devfeed.tech/tags/compiler.md>), [inlining](<https://devfeed.tech/tags/inlining.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [optimization](<https://devfeed.tech/tags/optimization.md>), [r8](<https://devfeed.tech/tags/r8.md>), [ssa](<https://devfeed.tech/tags/ssa.md>)

## AI overview

This article explains how R8 performs nullability data-flow analysis after inlining Kotlin functions. It uses an SSA-based intermediate representation to determine which branches are always taken or unreachable, enabling dead-code elimination and bytecode optimization.

## Source excerpt

Note: This post is part of a series on D8 and R8, Android's new dexer and optimizer, respectively. For an intro to D8 read "Android's Java 8 support". For an intro to R8 read "R8 Optimization: Staticization". The last post in this series was the first to cover R8 and one of its optimizations. This post will cover an optimization which performs data flow analysis of nullability. Let's dig in! A coalesce function returns the first non-null argument that is provided. Running the following example, unsurprisingly, prints "one" and then "two". fun <T : Any> coalesce(a: T?, b: T?): T? = a ?: b fun main(vararg args: String) { println(coalesce("one", "two")) println(coalesce(null, "two")) } R8 and ProGuard will both perform function inlining when a function is small or if it's only called in one place. Since coalesce is small, its body will be inlined to every call site to be equivalent to the following source. fun main(vararg args: String) { println("one" ?: "two") println(null ?: "two") } Were this actual source, the Kotlin compiler will determine that both of the elvis operators (?:) can be determined at compile-time. Compiling and dexing that fake source produces two calls to println with "one" and "two" and zero conditionals. [000180] NullsKt.main:([Ljava/lang/String;)V 0000: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; 0002: const-string v0, "one" 0004: invoke-virtual {v1, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V 0007: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; 0009: const-string v0, "two" 000b: invoke-virtual {v1, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/Object;)V 000e: return-void But since the inlining occurs inside of R8 and not prior to running the Kotlin compiler, the actual Dalvik bytecode contains the conditionals. [000144] NullsKt.main:([Ljava/lang/String;)V 0000: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; 0002: const-string v0, "one" 0004: if-nez v0, 0006 0006: const-string v0, "two" 0