[vm, gc] Extend alignment helpers to work with alignment offsets.

TEST=ci
Change-Id: I02bad1feba3a16360aa52414c94cdef81b158ed6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/262000
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h
index fb223da..087e042 100644
--- a/runtime/platform/utils.h
+++ b/runtime/platform/utils.h
@@ -73,35 +73,45 @@
   }
 
   template <typename T>
-  static constexpr bool IsAligned(T x, intptr_t n) {
-    assert(IsPowerOfTwo(n));
-    return (x & (n - 1)) == 0;
+  static constexpr bool IsAligned(T x,
+                                  uintptr_t alignment,
+                                  uintptr_t offset = 0) {
+    ASSERT(IsPowerOfTwo(alignment));
+    ASSERT(offset < alignment);
+    return (x & (alignment - 1)) == offset;
   }
 
   template <typename T>
-  static constexpr bool IsAligned(T* x, intptr_t n) {
-    return IsAligned(reinterpret_cast<uword>(x), n);
+  static constexpr bool IsAligned(T* x,
+                                  uintptr_t alignment,
+                                  uintptr_t offset = 0) {
+    return IsAligned(reinterpret_cast<uword>(x), alignment, offset);
   }
 
   template <typename T>
-  static constexpr inline T RoundDown(T x, intptr_t n) {
-    ASSERT(IsPowerOfTwo(n));
-    return (x & -n);
+  static constexpr inline T RoundDown(T x, intptr_t alignment) {
+    ASSERT(IsPowerOfTwo(alignment));
+    return (x & -alignment);
   }
 
   template <typename T>
-  static inline T* RoundDown(T* x, intptr_t n) {
-    return reinterpret_cast<T*>(RoundDown(reinterpret_cast<uword>(x), n));
+  static inline T* RoundDown(T* x, intptr_t alignment) {
+    return reinterpret_cast<T*>(
+        RoundDown(reinterpret_cast<uword>(x), alignment));
   }
 
   template <typename T>
-  static constexpr inline T RoundUp(T x, intptr_t n) {
-    return RoundDown(x + n - 1, n);
+  static constexpr inline T RoundUp(T x,
+                                    uintptr_t alignment,
+                                    uintptr_t offset = 0) {
+    ASSERT(offset < alignment);
+    return RoundDown(x + alignment - 1 + offset, alignment) - offset;
   }
 
   template <typename T>
-  static inline T* RoundUp(T* x, intptr_t n) {
-    return reinterpret_cast<T*>(RoundUp(reinterpret_cast<uword>(x), n));
+  static inline T* RoundUp(T* x, uintptr_t alignment, uintptr_t offset = 0) {
+    return reinterpret_cast<T*>(
+        RoundUp(reinterpret_cast<uword>(x), alignment, offset));
   }
 
   static uintptr_t RoundUpToPowerOfTwo(uintptr_t x);
diff --git a/runtime/vm/app_snapshot.h b/runtime/vm/app_snapshot.h
index 3445cb4..cf33a78 100644
--- a/runtime/vm/app_snapshot.h
+++ b/runtime/vm/app_snapshot.h
@@ -332,7 +332,9 @@
   void WriteBytes(const uint8_t* addr, intptr_t len) {
     stream_->WriteBytes(addr, len);
   }
-  void Align(intptr_t alignment) { stream_->Align(alignment); }
+  void Align(intptr_t alignment, intptr_t offset = 0) {
+    stream_->Align(alignment, offset);
+  }
 
   V8SnapshotProfileWriter::ObjectId GetProfileId(ObjectPtr object) const;
   V8SnapshotProfileWriter::ObjectId GetProfileId(intptr_t ref) const;
@@ -645,7 +647,9 @@
   }
 
   void Advance(intptr_t value) { stream_.Advance(value); }
-  void Align(intptr_t alignment) { stream_.Align(alignment); }
+  void Align(intptr_t alignment, intptr_t offset = 0) {
+    stream_.Align(alignment, offset);
+  }
 
   void AddBaseObject(ObjectPtr base_object) { AssignRef(base_object); }
 
diff --git a/runtime/vm/datastream.h b/runtime/vm/datastream.h
index 974cb88..4b9cfa1 100644
--- a/runtime/vm/datastream.h
+++ b/runtime/vm/datastream.h
@@ -128,9 +128,10 @@
     current_ = buffer_ + value;
   }
 
-  void Align(intptr_t alignment) {
+  void Align(intptr_t alignment, intptr_t offset = 0) {
     intptr_t position_before = Position();
-    intptr_t position_after = Utils::RoundUp(position_before, alignment);
+    intptr_t position_after =
+        Utils::RoundUp(position_before, alignment, offset);
     Advance(position_after - position_before);
   }
 
@@ -335,9 +336,10 @@
   DART_FORCE_INLINE intptr_t bytes_written() const { return Position(); }
   virtual intptr_t Position() const { return current_ - buffer_; }
 
-  intptr_t Align(intptr_t alignment) {
+  intptr_t Align(intptr_t alignment, intptr_t offset = 0) {
     const intptr_t position_before = Position();
-    const intptr_t position_after = Utils::RoundUp(position_before, alignment);
+    const intptr_t position_after =
+        Utils::RoundUp(position_before, alignment, offset);
     const intptr_t length = position_after - position_before;
     if (length != 0) {
       EnsureSpace(length);
diff --git a/runtime/vm/heap/page.h b/runtime/vm/heap/page.h
index 5aba1ae..08331b7 100644
--- a/runtime/vm/heap/page.h
+++ b/runtime/vm/heap/page.h
@@ -104,17 +104,15 @@
 
   void WriteProtect(bool read_only);
 
-  static intptr_t OldObjectStartOffset() {
-    return Utils::RoundUp(sizeof(Page) - kOldObjectAlignmentOffset,
-                          kObjectStartAlignment) +
-           kOldObjectAlignmentOffset;
+  constexpr static intptr_t OldObjectStartOffset() {
+    return Utils::RoundUp(sizeof(Page), kObjectStartAlignment,
+                          kOldObjectAlignmentOffset);
   }
-  static intptr_t NewObjectStartOffset() {
+  constexpr static intptr_t NewObjectStartOffset() {
     // Note weaker alignment because the bool/null offset tricks don't apply to
     // new-space.
-    return Utils::RoundUp(sizeof(Page) - kNewObjectAlignmentOffset,
-                          kObjectAlignment) +
-           kNewObjectAlignmentOffset;
+    return Utils::RoundUp(sizeof(Page), kObjectAlignment,
+                          kNewObjectAlignmentOffset);
   }
 
   // Warning: This does not work for objects on image pages because image pages
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index 01cd7af..297665f 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -710,7 +710,7 @@
       WriteTargetWord(FLAG_precompiled_mode ? Image::kHeaderSize
                                             : Image::kNoInstructionsSection);
   // Zero values for the rest of the Image object header bytes.
-  text_offset += Align(Image::kHeaderSize, text_offset);
+  text_offset += Align(Image::kHeaderSize, 0, text_offset);
   ASSERT_EQUAL(text_offset, Image::kHeaderSize);
 
 #if defined(DART_PRECOMPILER)
@@ -776,11 +776,14 @@
         bare_instruction_payloads
             ? compiler::target::Instructions::kBarePayloadAlignment
             : compiler::target::ObjectAlignment::kObjectAlignment;
+    const intptr_t alignment_offset =
+        compiler::target::ObjectAlignment::kOldObjectAlignmentOffset;
     const intptr_t expected_size =
         bare_instruction_payloads
             ? compiler::target::InstructionsSection::HeaderSize()
             : compiler::target::InstructionsSection::InstanceSize(0);
-    text_offset += Align(section_contents_alignment, text_offset);
+    text_offset +=
+        Align(section_contents_alignment, alignment_offset, text_offset);
     ASSERT_EQUAL(text_offset - id.nonce(), expected_size);
   }
 #endif
@@ -837,6 +840,7 @@
       text_offset += WriteFixed(insns.untag()->size_and_flags_);
       text_offset +=
           Align(compiler::target::Instructions::kNonBarePayloadAlignment,
+                compiler::target::ObjectAlignment::kOldObjectAlignmentOffset,
                 text_offset);
     }
 
@@ -1367,7 +1371,7 @@
   if (global_symbol) {
     assembly_stream_->Printf(".globl %s\n", current_section_symbol_);
   }
-  intptr_t padding = Align(alignment);
+  intptr_t padding = Align(alignment, 0, 0);
   if (alignment_padding != nullptr) {
     *alignment_padding = padding;
   }
@@ -1599,7 +1603,10 @@
   return size;
 }
 
-intptr_t AssemblyImageWriter::Align(intptr_t alignment, intptr_t position) {
+intptr_t AssemblyImageWriter::Align(intptr_t alignment,
+                                    intptr_t offset,
+                                    intptr_t position) {
+  ASSERT(offset == 0);
   const intptr_t next_position = Utils::RoundUp(position, alignment);
   assembly_stream_->Printf(".balign %" Pd ", 0\n", alignment);
   return next_position - position;
@@ -1731,10 +1738,14 @@
   return compiler::target::kWordSize;
 }
 
-intptr_t BlobImageWriter::Align(intptr_t alignment, intptr_t offset) {
-  const intptr_t stream_padding = current_section_stream_->Align(alignment);
-  // Double-check that the offset has the same alignment.
-  ASSERT_EQUAL(Utils::RoundUp(offset, alignment) - offset, stream_padding);
+intptr_t BlobImageWriter::Align(intptr_t alignment,
+                                intptr_t offset,
+                                intptr_t position) {
+  const intptr_t stream_padding =
+      current_section_stream_->Align(alignment, offset);
+  // Double-check that the position has the same alignment.
+  ASSERT_EQUAL(Utils::RoundUp(position, alignment, offset) - position,
+               stream_padding);
   return stream_padding;
 }
 
diff --git a/runtime/vm/image_snapshot.h b/runtime/vm/image_snapshot.h
index abbcabf..fa755fd 100644
--- a/runtime/vm/image_snapshot.h
+++ b/runtime/vm/image_snapshot.h
@@ -442,7 +442,9 @@
   // section contents.
   virtual intptr_t WriteBytes(const void* bytes, intptr_t size) = 0;
   // Pads the section contents to a given alignment with zeroes.
-  virtual intptr_t Align(intptr_t alignment, intptr_t offset) = 0;
+  virtual intptr_t Align(intptr_t alignment,
+                         intptr_t offset,
+                         intptr_t position) = 0;
 #if defined(DART_PRECOMPILER)
   // Writes a target word-sized value that depends on the final relocated
   // addresses of the sections named by the two symbols. If T is the final
@@ -610,7 +612,9 @@
   virtual void ExitSection(ProgramSection name, bool vm, intptr_t size);
   virtual intptr_t WriteTargetWord(word value);
   virtual intptr_t WriteBytes(const void* bytes, intptr_t size);
-  virtual intptr_t Align(intptr_t alignment, intptr_t offset = 0);
+  virtual intptr_t Align(intptr_t alignment,
+                         intptr_t offset,
+                         intptr_t position);
   virtual intptr_t Relocation(intptr_t section_offset,
                               const char* source_symbol,
                               intptr_t source_offset,
@@ -662,7 +666,9 @@
   virtual void ExitSection(ProgramSection name, bool vm, intptr_t size);
   virtual intptr_t WriteTargetWord(word value);
   virtual intptr_t WriteBytes(const void* bytes, intptr_t size);
-  virtual intptr_t Align(intptr_t alignment, intptr_t offset);
+  virtual intptr_t Align(intptr_t alignment,
+                         intptr_t offset,
+                         intptr_t position);
   // TODO(rmacnak): Generate .debug_frame / .eh_frame / .arm.exidx to
   // provide unwinding information.
   virtual void FrameUnwindPrologue() {}
diff --git a/runtime/vm/utils_test.cc b/runtime/vm/utils_test.cc
index 2b0369f..8cc9cb8 100644
--- a/runtime/vm/utils_test.cc
+++ b/runtime/vm/utils_test.cc
@@ -110,6 +110,13 @@
   EXPECT(Utils::IsAligned(32, 8));
   EXPECT(!Utils::IsAligned(33, 8));
   EXPECT(Utils::IsAligned(40, 8));
+
+  EXPECT(!Utils::IsAligned(0, 8, 4));
+  EXPECT(!Utils::IsAligned(1, 8, 4));
+  EXPECT(Utils::IsAligned(4, 8, 4));
+  EXPECT(!Utils::IsAligned(8, 8, 4));
+  EXPECT(!Utils::IsAligned(9, 8, 4));
+  EXPECT(Utils::IsAligned(12, 8, 4));
 }
 
 VM_UNIT_TEST_CASE(RoundDown) {
@@ -128,6 +135,13 @@
   uword* address = reinterpret_cast<uword*>(63);
   uword* roundup_address = reinterpret_cast<uword*>(64);
   EXPECT_EQ(roundup_address, Utils::RoundUp(address, 32));
+
+  EXPECT_EQ(4, Utils::RoundUp(0, 8, 4));
+  EXPECT_EQ(4, Utils::RoundUp(1, 8, 4));
+  EXPECT_EQ(4, Utils::RoundUp(4, 8, 4));
+  EXPECT_EQ(12, Utils::RoundUp(8, 8, 4));
+  EXPECT_EQ(12, Utils::RoundUp(9, 8, 4));
+  EXPECT_EQ(12, Utils::RoundUp(12, 8, 4));
 }
 
 VM_UNIT_TEST_CASE(RoundUpToPowerOfTwo) {