text: #ifdef COMPILING_HALIDE_RUNTIME
  #include "HalideRuntime.h"
  #define HALIDE_BUFFER_HELPER_ATTRS WEAK_INLINE
  #else
  #define HALIDE_BUFFER_HELPER_ATTRS inline
  #endif
  
  // Structs are annoying to deal with from within Halide Stmts. These
  // utility functions are for dealing with halide_buffer_t in that
  // context. They are not intended for use outside of Halide code, and
  // not exposed in HalideRuntime.h. The symbols are private to the
  // module and should be inlined and then stripped. This blob of code
  // also gets copy-pasted into C outputs.
  
  extern "C" {
  
  HALIDE_BUFFER_HELPER_ATTRS
  int _halide_buffer_get_dimensions(const halide_buffer_t *buf) {
      return buf->dimensions;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  uint8_t *_halide_buffer_get_host(const halide_buffer_t *buf) {
      return buf->host;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  uint64_t _halide_buffer_get_device(const halide_buffer_t *buf) {
      return buf->device;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  const struct halide_device_interface_t *_halide_buffer_get_device_interface(const halide_buffer_t *buf) {
      return buf->device_interface;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  int _halide_buffer_get_min(const halide_buffer_t *buf, int d) {
      return buf->dim[d].min;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  int _halide_buffer_get_max(const halide_buffer_t *buf, int d) {
      return buf->dim[d].min + buf->dim[d].extent - 1;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  int _halide_buffer_get_extent(const halide_buffer_t *buf, int d) {
      return buf->dim[d].extent;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  int _halide_buffer_get_stride(const halide_buffer_t *buf, int d) {
      return buf->dim[d].stride;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  int _halide_buffer_set_host_dirty(halide_buffer_t *buf, bool val) {
      buf->set_host_dirty(val);
      return 0;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  int _halide_buffer_set_device_dirty(halide_buffer_t *buf, bool val) {
      buf->set_device_dirty(val);
      return 0;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  bool _halide_buffer_get_host_dirty(const halide_buffer_t *buf) {
      return buf->host_dirty();
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  bool _halide_buffer_get_device_dirty(const halide_buffer_t *buf) {
      return buf->device_dirty();
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  halide_dimension_t *_halide_buffer_get_shape(halide_buffer_t *buf) {
      return buf->dim;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  bool _halide_buffer_is_bounds_query(const halide_buffer_t *buf) {
      return buf->host == nullptr && buf->device == 0;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  uint32_t _halide_buffer_get_type(const halide_buffer_t *buf) {
      return buf->type.as_u32();
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  halide_buffer_t *_halide_buffer_init(halide_buffer_t *dst,
                                       halide_dimension_t *dst_shape,
                                       void *host,
                                       uint64_t device,
                                       const halide_device_interface_t *device_interface,
                                       int type_code, int type_bits,
                                       int dimensions,
                                       halide_dimension_t *shape,
                                       uint64_t flags) {
      dst->host = (uint8_t *)host;
      dst->device = device;
      dst->device_interface = device_interface;
      dst->type.code = (halide_type_code_t)type_code;
      dst->type.bits = (uint8_t)type_bits;
      dst->type.lanes = 1;
      dst->dimensions = dimensions;
      dst->dim = dst_shape;
      if (shape != dst->dim) {
          for (int i = 0; i < dimensions; i++) {
              dst->dim[i] = shape[i];
          }
      }
      dst->flags = flags;
      return dst;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  halide_buffer_t *_halide_buffer_init_from_buffer(halide_buffer_t *dst,
                                                   halide_dimension_t *dst_shape,
                                                   const halide_buffer_t *src) {
      dst->host = src->host;
      dst->device = src->device;
      dst->device_interface = src->device_interface;
      dst->type = src->type;
      dst->dimensions = src->dimensions;
      dst->dim = dst_shape;
      dst->flags = src->flags;
      for (int i = 0; i < dst->dimensions; i++) {
          dst->dim[i] = src->dim[i];
      }
      return dst;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  halide_buffer_t *_halide_buffer_crop(void *user_context,
                                       halide_buffer_t *dst,
                                       halide_dimension_t *dst_shape,
                                       const halide_buffer_t *src,
                                       const int *min, const int *extent) {
      *dst = *src;
      dst->dim = dst_shape;
      int64_t offset = 0;
      for (int i = 0; i < dst->dimensions; i++) {
          dst->dim[i] = src->dim[i];
          dst->dim[i].min = min[i];
          dst->dim[i].extent = extent[i];
          offset += (min[i] - src->dim[i].min) * (int64_t)src->dim[i].stride;
      }
      if (dst->host) {
          dst->host += offset * src->type.bytes();
      }
      dst->device_interface = nullptr;
      dst->device = 0;
      if (src->device_interface) {
          if (src->device_interface->device_crop(user_context, src, dst) != 0) {
              // This is uncommon: either a runtime error, or a backend that
              // doesn't replace the default definition of device_crop. But it
              // does happen, so let's return a nullptr here, and require the caller
              // to check the result.
              return nullptr;
          }
      }
      return dst;
  }
  
  // Called on return from an extern stage where the output buffer was a
  // crop of some other larger buffer. This happens for extern stages
  // with distinct store_at/compute_at levels. Each call to the stage
  // only fills in part of the buffer.
  HALIDE_BUFFER_HELPER_ATTRS
  int _halide_buffer_retire_crop_after_extern_stage(void *user_context,
                                                    void *obj) {
      int result;
      halide_buffer_t **buffers = (halide_buffer_t **)obj;
      halide_buffer_t *crop = buffers[0];
      halide_buffer_t *parent = buffers[1];
  
      if (crop->device) {
          if (!parent->device) {
              // We have been given a device allocation by the extern
              // stage. It only represents the cropped region, so we
              // can't just give it to the parent.
              if (crop->device_dirty()) {
                  result = crop->device_interface->copy_to_host(user_context, crop);
                  if (result != 0) {
                      return result;
                  }
              }
              result = crop->device_interface->device_free(user_context, crop);
              if (result != 0) {
                  return result;
              }
          } else {
              // We are a crop of an existing device allocation.
              if (crop->device_dirty()) {
                  parent->set_device_dirty();
              }
              result = crop->device_interface->device_release_crop(user_context, crop);
              if (result != 0) {
                  return result;
              }
          }
      }
      if (crop->host_dirty()) {
          parent->set_host_dirty();
      }
      return 0;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  int _halide_buffer_retire_crops_after_extern_stage(void *user_context,
                                                     void *obj) {
      halide_buffer_t **buffers = (halide_buffer_t **)obj;
      while (*buffers) {
          _halide_buffer_retire_crop_after_extern_stage(user_context, buffers);
          buffers += 2;
      }
      return 0;
  }
  
  HALIDE_BUFFER_HELPER_ATTRS
  halide_buffer_t *_halide_buffer_set_bounds(halide_buffer_t *buf,
                                             int dim, int min, int extent) {
      // This can be called with the result of _halide_buffer_crop(), which
      // can return nullptr if an error occurs -- so don't crash, just propagate
      // the nullptr result to our caller.
      if (buf != nullptr) {
          buf->dim[dim].min = min;
          buf->dim[dim].extent = extent;
      }
      return buf;
  }
  }
  
  #undef HALIDE_BUFFER_HELPER_ATTRS
  