pw_result: Add some size reports

This adds some simple size reports to the result module to provide some
data on how it compares to traditional function prototypes.

Change-Id: I711fa5d04ff5720ed3184b6250f3993606bd34ec
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/17162
Commit-Queue: Alexei Frolov <frolv@google.com>
Reviewed-by: Alexei Frolov <frolv@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_preprocessor/public/pw_preprocessor/compiler.h b/pw_preprocessor/public/pw_preprocessor/compiler.h
index 8f87dbd..9d7d03d 100644
--- a/pw_preprocessor/public/pw_preprocessor/compiler.h
+++ b/pw_preprocessor/public/pw_preprocessor/compiler.h
@@ -74,6 +74,9 @@
 //
 #define PW_NO_RETURN __attribute__((noreturn))
 
+// Prevents the compiler from inlining a fuction.
+#define PW_NO_INLINE __attribute__((noinline))
+
 // Indicate to the compiler that the given section of code will not be reached.
 // Example:
 //
diff --git a/pw_result/BUILD.gn b/pw_result/BUILD.gn
index b839de8..db338ae 100644
--- a/pw_result/BUILD.gn
+++ b/pw_result/BUILD.gn
@@ -15,6 +15,7 @@
 # gn-format disable
 import("//build_overrides/pigweed.gni")
 
+import("$dir_pw_bloat/bloat.gni")
 import("$dir_pw_build/target_types.gni")
 import("$dir_pw_docgen/docs.gni")
 import("$dir_pw_unit_test/test.gni")
@@ -42,4 +43,27 @@
 
 pw_doc_group("docs") {
   sources = [ "docs.rst" ]
+  report_deps = [ ":result_size" ]
+}
+
+pw_size_report("result_size") {
+  title = "pw::Result vs. pw::Status and out pointer"
+
+  binaries = [
+    {
+      target = "size_report:result_simple"
+      base = "size_report:pointer_simple"
+      label = "Simple function"
+    },
+    {
+      target = "size_report:result_noinline"
+      base = "size_report:pointer_noinline"
+      label = "Simple function without inlining"
+    },
+    {
+      target = "size_report:result_read"
+      base = "size_report:pointer_read"
+      label = "Returning a larger object (std::span)"
+    },
+  ]
 }
diff --git a/pw_result/docs.rst b/pw_result/docs.rst
index 5671d8a..02d6854 100644
--- a/pw_result/docs.rst
+++ b/pw_result/docs.rst
@@ -24,3 +24,15 @@
 Compatibility
 =============
 Works with C++11, but some features require C++17.
+
+Size report
+===========
+The table below showcases the difference in size between functions returning a
+Status with an output pointer, and functions returning a Result, in various
+situations.
+
+Note that these are simplified examples which do not necessarily reflect the
+usage of Result in real code. Make sure to always run your own size reports to
+check if Result is suitable for you.
+
+.. include:: result_size
diff --git a/pw_result/size_report/BUILD b/pw_result/size_report/BUILD
new file mode 100644
index 0000000..1a67937
--- /dev/null
+++ b/pw_result/size_report/BUILD
@@ -0,0 +1,78 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+load(
+    "//pw_build:pigweed.bzl",
+    "pw_cc_binary",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # Apache License 2.0
+
+pw_cc_binary(
+    name = "pointer_simple",
+    srcs = ["pointer_simple.cc"],
+    deps = [
+        "//pw_result",
+        "//pw_log",
+    ],
+)
+
+pw_cc_binary(
+    name = "result_simple",
+    srcs = ["result_simple.cc"],
+    deps = [
+        "//pw_result",
+        "//pw_log",
+    ],
+)
+
+pw_cc_binary(
+    name = "pointer_noinline",
+    srcs = ["pointer_noinline.cc"],
+    deps = [
+        "//pw_result",
+        "//pw_log",
+    ],
+)
+
+pw_cc_binary(
+    name = "result_noinline",
+    srcs = ["result_noinline.cc"],
+    deps = [
+        "//pw_result",
+        "//pw_log",
+    ],
+)
+
+pw_cc_binary(
+    name = "pointer_read",
+    srcs = ["pointer_read.cc"],
+    deps = [
+        "//pw_result",
+        "//pw_log",
+        "//pw_span",
+    ],
+)
+
+pw_cc_binary(
+    name = "result_read",
+    srcs = ["result_read.cc"],
+    deps = [
+        "//pw_result",
+        "//pw_log",
+        "//pw_span",
+    ],
+)
diff --git a/pw_result/size_report/BUILD.gn b/pw_result/size_report/BUILD.gn
new file mode 100644
index 0000000..34556d3
--- /dev/null
+++ b/pw_result/size_report/BUILD.gn
@@ -0,0 +1,73 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# gn-format disable
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+pw_executable("pointer_simple") {
+  sources = [ "pointer_simple.cc" ]
+  deps = [
+    "..",
+    dir_pw_log,
+  ]
+}
+
+pw_executable("result_simple") {
+  sources = [ "result_simple.cc" ]
+  deps = [
+    "..",
+    dir_pw_log,
+  ]
+}
+
+pw_executable("pointer_noinline") {
+  sources = [ "pointer_noinline.cc" ]
+  deps = [
+    "..",
+    dir_pw_log,
+    dir_pw_preprocessor,
+  ]
+}
+
+pw_executable("result_noinline") {
+  sources = [ "result_noinline.cc" ]
+  deps = [
+    "..",
+    dir_pw_log,
+    dir_pw_preprocessor,
+  ]
+}
+
+pw_executable("pointer_read") {
+  sources = [ "pointer_read.cc" ]
+  deps = [
+    "..",
+    dir_pw_bytes,
+    dir_pw_log,
+    dir_pw_preprocessor,
+    dir_pw_span,
+  ]
+}
+
+pw_executable("result_read") {
+  sources = [ "result_read.cc" ]
+  deps = [
+    "..",
+    dir_pw_bytes,
+    dir_pw_log,
+    dir_pw_preprocessor,
+    dir_pw_span,
+  ]
+}
diff --git a/pw_result/size_report/pointer_noinline.cc b/pw_result/size_report/pointer_noinline.cc
new file mode 100644
index 0000000..7c77de6
--- /dev/null
+++ b/pw_result/size_report/pointer_noinline.cc
@@ -0,0 +1,37 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_log/log.h"
+#include "pw_preprocessor/compiler.h"
+#include "pw_status/status.h"
+
+PW_NO_INLINE pw::Status Divide(float a, float b, float* out) {
+  if (b == 0) {
+    return pw::Status::INVALID_ARGUMENT;
+  }
+  *out = a / b;
+  return pw::Status::OK;
+}
+
+int volatile* unoptimizable;
+
+int main() {
+  float f;
+  if (Divide(*unoptimizable, *unoptimizable, &f).ok()) {
+    PW_LOG_INFO("result is %f", f);
+    return 0;
+  }
+
+  return 1;
+}
diff --git a/pw_result/size_report/pointer_read.cc b/pw_result/size_report/pointer_read.cc
new file mode 100644
index 0000000..e346452
--- /dev/null
+++ b/pw_result/size_report/pointer_read.cc
@@ -0,0 +1,57 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <cstring>
+#include <span>
+
+#include "pw_bytes/array.h"
+#include "pw_log/log.h"
+#include "pw_preprocessor/compiler.h"
+#include "pw_status/status.h"
+
+namespace {
+
+// clang-format off
+constexpr auto kArray = pw::bytes::Array<
+    0x0a, 0x14, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00,
+    0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x96, 0x00,
+    0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x12, 0x08,
+    0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01>();
+// clang-format on
+
+PW_NO_INLINE pw::Status Read(size_t offset,
+                             size_t size,
+                             std::span<const std::byte>* out) {
+  if (offset + size >= std::size(kArray)) {
+    return pw::Status::OUT_OF_RANGE;
+  }
+
+  *out = std::span<const std::byte>(std::data(kArray) + offset, size);
+  return pw::Status::OK;
+}
+
+}  // namespace
+
+size_t volatile* unoptimizable;
+
+int main() {
+  std::span<const std::byte> data;
+  pw::Status status = Read(*unoptimizable, *unoptimizable, &data);
+  if (!status.ok()) {
+    return 1;
+  }
+
+  PW_LOG_INFO("Read %u bytes", static_cast<unsigned>(data.size()));
+  return 0;
+}
diff --git a/pw_result/size_report/pointer_simple.cc b/pw_result/size_report/pointer_simple.cc
new file mode 100644
index 0000000..eb536d4
--- /dev/null
+++ b/pw_result/size_report/pointer_simple.cc
@@ -0,0 +1,36 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_log/log.h"
+#include "pw_status/status.h"
+
+pw::Status Divide(float a, float b, float* out) {
+  if (b == 0) {
+    return pw::Status::INVALID_ARGUMENT;
+  }
+  *out = a / b;
+  return pw::Status::OK;
+}
+
+int volatile* unoptimizable;
+
+int main() {
+  float f;
+  if (Divide(*unoptimizable, *unoptimizable, &f).ok()) {
+    PW_LOG_INFO("result is %f", f);
+    return 0;
+  }
+
+  return 1;
+}
diff --git a/pw_result/size_report/result_noinline.cc b/pw_result/size_report/result_noinline.cc
new file mode 100644
index 0000000..c099dc5
--- /dev/null
+++ b/pw_result/size_report/result_noinline.cc
@@ -0,0 +1,35 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_log/log.h"
+#include "pw_preprocessor/compiler.h"
+#include "pw_result/result.h"
+
+PW_NO_INLINE pw::Result<float> Divide(float a, float b) {
+  if (b == 0) {
+    return pw::Status::INVALID_ARGUMENT;
+  }
+  return a / b;
+}
+
+float volatile* unoptimizable;
+
+int main() {
+  if (pw::Result result = Divide(*unoptimizable, *unoptimizable); result.ok()) {
+    PW_LOG_INFO("result is %f", result.value());
+    return 0;
+  }
+
+  return 1;
+}
diff --git a/pw_result/size_report/result_read.cc b/pw_result/size_report/result_read.cc
new file mode 100644
index 0000000..9bc9ffd
--- /dev/null
+++ b/pw_result/size_report/result_read.cc
@@ -0,0 +1,54 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include <cstring>
+#include <span>
+
+#include "pw_bytes/array.h"
+#include "pw_log/log.h"
+#include "pw_preprocessor/compiler.h"
+#include "pw_result/result.h"
+
+namespace {
+
+// clang-format off
+constexpr auto kArray = pw::bytes::Array<
+    0x0a, 0x14, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00,
+    0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x96, 0x00,
+    0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x12, 0x08,
+    0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01>();
+// clang-format on
+
+PW_NO_INLINE pw::Result<std::span<const std::byte>> Read(size_t offset,
+                                                         size_t size) {
+  if (offset + size >= std::size(kArray)) {
+    return pw::Status::OUT_OF_RANGE;
+  }
+
+  return std::span<const std::byte>(std::data(kArray) + offset, size);
+}
+
+}  // namespace
+
+size_t volatile* unoptimizable;
+
+int main() {
+  pw::Result result = Read(*unoptimizable, *unoptimizable);
+  if (!result.ok()) {
+    return 1;
+  }
+
+  PW_LOG_INFO("Read %u bytes", static_cast<unsigned>(result.value().size()));
+  return 0;
+}
diff --git a/pw_result/size_report/result_simple.cc b/pw_result/size_report/result_simple.cc
new file mode 100644
index 0000000..91772b9
--- /dev/null
+++ b/pw_result/size_report/result_simple.cc
@@ -0,0 +1,34 @@
+// Copyright 2020 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_log/log.h"
+#include "pw_result/result.h"
+
+pw::Result<float> Divide(float a, float b) {
+  if (b == 0) {
+    return pw::Status::INVALID_ARGUMENT;
+  }
+  return a / b;
+}
+
+float volatile* unoptimizable;
+
+int main() {
+  if (pw::Result result = Divide(*unoptimizable, *unoptimizable); result.ok()) {
+    PW_LOG_INFO("result is %f", result.value());
+    return 0;
+  }
+
+  return 1;
+}