[llvm] [llubi] Implements common library functions (PR #190147)

Antonio Frighetto via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 2 06:55:17 PDT 2026


================
@@ -0,0 +1,348 @@
+//===- Library.cpp - Library calls for llubi ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements common libcalls for llubi.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Library.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/IR/InstrTypes.h"
+
+namespace llvm::ubi {
+
+static uint64_t getMaxAlignT(const DataLayout &DL) {
+  return DL.getPointerABIAlignment(0).value() >= 8 ? 16 : 8;
+}
+
+Library::Library(Context &Ctx, EventHandler &Handler, const DataLayout &DL,
+                 ExecutorBase &Executor)
+    : Ctx(Ctx), Handler(Handler), DL(DL), Executor(Executor) {}
+
+std::optional<std::string> Library::readStringFromMemory(const Pointer &Ptr) {
+  auto *MO = Ptr.getMemoryObject();
+  if (!MO) {
+    Executor.reportImmediateUB(
+        "Invalid memory access via a pointer with nullary "
+        "provenance.");
+    return std::nullopt;
+  }
+
+  std::string Result;
+  const uint64_t Address = Ptr.address().getZExtValue();
+  uint64_t Offset = 0;
+
+  while (true) {
+    auto ValidOffset = Executor.verifyMemAccess(
+        *MO, APInt(DL.getPointerSizeInBits(0), Address + Offset), 1, Align(1),
+        false);
+    if (!ValidOffset) {
+      return std::nullopt;
+    }
+
+    Byte B = (*MO)[*ValidOffset];
+    if (B.ConcreteMask != 0xFF) {
+      Executor.reportImmediateUB("Read uninitialized or poison memory while "
+                                 "parsing C-string.");
+      return std::nullopt;
+    }
+
+    if (B.Value == 0) {
+      break;
+    }
+
+    Result.push_back(static_cast<char>(B.Value));
+    ++Offset;
+  }
+
+  return Result;
+}
+
+AnyValue Library::executeMalloc(StringRef Name, Type *Type,
+                                ArrayRef<AnyValue> Args) {
+  const auto &SizeVal = Args[0];
+  if (SizeVal.isPoison()) {
+    Executor.reportImmediateUB("malloc() called with a poison size.");
+    return AnyValue::poison();
+  }
+
+  const uint64_t AllocSize = SizeVal.asInteger().getZExtValue();
+  const uint64_t MaxAlign = getMaxAlignT(DL);
+
+  const auto Obj =
+      Ctx.allocate(AllocSize, MaxAlign, Name, 0, MemInitKind::Uninitialized);
+
+  if (!Obj)
+    return AnyValue::getNullValue(Ctx, Type);
+
+  return Ctx.deriveFromMemoryObject(Obj);
+}
+
+AnyValue Library::executeCalloc(StringRef Name, Type *Type,
+                                ArrayRef<AnyValue> Args) {
+  const auto &CountVal = Args[0];
+  const auto &SizeVal = Args[1];
+
+  if (CountVal.isPoison()) {
+    Executor.reportImmediateUB("calloc() called with a poison count.");
+    return AnyValue::poison();
+  }
+  if (SizeVal.isPoison()) {
+    Executor.reportImmediateUB("calloc() called with a poison size.");
+    return AnyValue::poison();
+  }
+
+  const uint64_t Count = CountVal.asInteger().getZExtValue();
+  const uint64_t Size = SizeVal.asInteger().getZExtValue();
+
+  bool Overflow;
+  const uint64_t AllocSize = SaturatingMultiply(Count, Size, &Overflow);
+  if (Overflow) {
+    return AnyValue::getNullValue(Ctx, Type);
+  }
+
+  const uint64_t MaxAlign = getMaxAlignT(DL);
+
+  // TODO: Figure out how to name the allocation
+  const auto Obj =
+      Ctx.allocate(AllocSize, MaxAlign, Name, 0, MemInitKind::Zeroed);
+
+  if (!Obj) {
+    return AnyValue::getNullValue(Ctx, Type);
+  }
+
+  return Ctx.deriveFromMemoryObject(Obj);
+}
+
+AnyValue Library::executeFree(StringRef Name, Type *Type,
+                              ArrayRef<AnyValue> Args) {
+  const auto &PtrVal = Args[0];
+  if (PtrVal.isPoison()) {
+    Executor.reportImmediateUB("free() called with a poison pointer.");
+    return AnyValue::poison();
+  }
+
+  auto &Ptr = PtrVal.asPointer();
+  if (Ptr.address().isZero()) {
+    // no-op when free is called with a null pointer.
+    return AnyValue();
+  }
+
+  if (!Ctx.free(Ptr.address().getZExtValue())) {
+    Executor.reportImmediateUB(
+        "freeing an invalid, unallocated, or already freed pointer.");
+    return AnyValue::poison();
+  }
+
+  return AnyValue();
+}
+
+AnyValue Library::executePuts(StringRef Name, Type *Type,
+                              ArrayRef<AnyValue> Args) {
+  const auto &PtrVal = Args[0];
+  if (PtrVal.isPoison()) {
+    Executor.reportImmediateUB("puts called with a poison pointer.");
+    return AnyValue::poison();
+  }
+
+  const auto StrOpt = readStringFromMemory(PtrVal.asPointer());
+  if (!StrOpt) {
+    return AnyValue::poison();
+  }
+
+  Handler.onPrint(*StrOpt + "\n");
+  return AnyValue(APInt(32, 1));
+}
+
+AnyValue Library::executePrintf(StringRef Name, Type *Type,
+                                ArrayRef<AnyValue> Args) {
+  const auto &FormatPtrVal = Args[0];
+  if (FormatPtrVal.isPoison()) {
+    Executor.reportImmediateUB(
+        "printf called with a poison format string pointer.");
+    return AnyValue::poison();
+  }
+
+  const auto FormatStrOpt = readStringFromMemory(FormatPtrVal.asPointer());
+  if (!FormatStrOpt) {
+    return AnyValue::poison();
+  }
+
+  const std::string FormatStr = *FormatStrOpt;
+  std::string Output;
+  unsigned ArgIndex = 1; // Start from 1 since 0 is the format string.
+
+  for (size_t i = 0; i < FormatStr.size();) {
----------------
antoniofrighetto wrote:

```suggestion
  for (unsigned I = 0; I < FormatStr.size(); ) {
```

https://github.com/llvm/llvm-project/pull/190147


More information about the llvm-commits mailing list