diff --git a/ivette/src/dome/renderer/graph/diagram.tsx b/ivette/src/dome/renderer/graph/diagram.tsx
index f2c2c732aff6a8b1d98bca85ed4c2dcc589fc3b3..ec0325a9dcc403b3da70add407567c299832114e 100644
--- a/ivette/src/dome/renderer/graph/diagram.tsx
+++ b/ivette/src/dome/renderer/graph/diagram.tsx
@@ -35,6 +35,8 @@ import './style.css';
 
 export type Direction = 'LR' | 'TD';
 
+export type Font = 'serif' | 'sans' | 'mono';
+
 export type Color =
   | 'white' | 'grey' | 'dark'
   | 'primary' | 'selected'
@@ -45,7 +47,10 @@ export type Shape =
   | 'point' | 'box'
   | 'diamond' | 'hexagon'
   | 'circle' | 'ellipse'
-  | 'note' | 'tab' | 'folder';
+  | 'note' | 'tab' | 'folder' | 'cds';
+
+export type Anchor =
+  'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw' | 'c' | '_';
 
 export type Arrow = 'none' | 'arrow' | 'tee' | 'box' | 'dot';
 
@@ -64,6 +69,8 @@ export interface Node {
   label?: string;
   /** Node tooltip */
   title?: string;
+  /** Node font (label) */
+  font?: Font;
   /** Node color (filled background) */
   color?: Color;
   /**
@@ -90,18 +97,19 @@ export interface Edge {
   line?: Line;
   /** Default is `dark` */
   color?: Color;
-  /** Default is `arrow` */
   head?: Arrow;
-  /** Default is `none` */
+  headLabel?: string,
+  headAnchor?: Anchor;
   tail?: Arrow;
-  /** Label */
+  tailLabel?: string,
+  tailAnchor?: Anchor;
+  font?: Font;
   label?: string;
-  /** Tooltip */
   title?: string;
-  /** Head label */
-  headLabel?: string,
-  /** Tail label */
-  tailLabel?: string,
+  /** Edge constraints node placement (default: true). */
+  constraint?: boolean;
+  /** Node placement on the same rank (default: false). */
+  aligned?: boolean;
 }
 
 export interface Cluster {
@@ -148,9 +156,15 @@ export interface DiagramProps {
 }
 
 /* -------------------------------------------------------------------------- */
-/* --- Color Model                                                        --- */
+/* --- CSS Model                                                          --- */
 /* -------------------------------------------------------------------------- */
 
+const FONTNAME = {
+  'serif': 'Times',
+  'sans': 'sans-serif',
+  'mono': 'Courier',
+};
+
 // node background colors
 const BGCOLOR = {
   'white': '#fff',
@@ -307,7 +321,10 @@ class Builder {
   }
 
   attr(a: string, v: string | number | boolean | undefined): Builder {
-    return v ? this.print(' ', a, '=').value(v).print(';') : this;
+    if (v !== undefined && v !== '')
+      return this.print(' ', a, '=').value(v).print(';');
+    else
+      return this;
   }
 
   // --- Node Table Shape
@@ -340,7 +357,8 @@ class Builder {
       .print('  ')
       .port(n.id)
       .print(' [')
-      .attr('id', n.id);
+      .attr('id', n.id)
+      .attr('fontname', n.font ? FONTNAME[n.font] : undefined);
     if (typeof n.shape === 'object') {
       this
         .attr('shape', 'record')
@@ -392,6 +410,11 @@ class Builder {
   edge(e: Edge): void {
     const { line = 'solid', head = 'arrow', tail = 'none' } = e;
     const tooltip = e.title ?? e.label ?? `${e.source} -> ${e.target}`;
+    if (e.aligned === true)
+      this
+        .print('{ rank=same; ')
+        .port(e.source).print(' ')
+        .port(e.target).println(' };');
     this
       .print('  ')
       .port(e.source, e.sourcePort)
@@ -399,8 +422,12 @@ class Builder {
       .port(e.target, e.targetPort)
       .print(' [')
       .attr('label', e.label)
+      .attr('fontname', e.font ? FONTNAME[e.font] : undefined)
+      .attr('headport', e.tailAnchor)
+      .attr('tailport', e.headAnchor)
       .attr('headlabel', e.headLabel)
       .attr('taillabel', e.tailLabel)
+      .attr('constraint', e.constraint === false ? false : undefined)
       .attr('labeltooltip', e.label ? tooltip : undefined)
       .attr('headtooltip', e.headLabel ? tooltip : undefined)
       .attr('tailtooltip', e.tailLabel ? tooltip : undefined)
@@ -476,7 +503,7 @@ function GraphvizView(props: GraphvizProps): JSX.Element {
     setError(undefined);
     graphviz(href, {
       useWorker: false,
-      fit: true, zoom: true, width, height,
+      fit: false, zoom: true, width, height,
     }).onerror(setError)
       .renderDot(model).on('end', function () {
         if (onSelection) {
diff --git a/ivette/src/frama-c/plugins/region/api/index.ts b/ivette/src/frama-c/plugins/region/api/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5d01bdbe22ac529ecefaa43e5a11963c12441397
--- /dev/null
+++ b/ivette/src/frama-c/plugins/region/api/index.ts
@@ -0,0 +1,199 @@
+/* ************************************************************************ */
+/*                                                                          */
+/*   This file is part of Frama-C.                                          */
+/*                                                                          */
+/*   Copyright (C) 2007-2024                                                */
+/*     CEA (Commissariat à l'énergie atomique et aux énergies               */
+/*          alternatives)                                                   */
+/*                                                                          */
+/*   you can redistribute it and/or modify it under the terms of the GNU    */
+/*   Lesser General Public License as published by the Free Software        */
+/*   Foundation, version 2.1.                                               */
+/*                                                                          */
+/*   It is distributed in the hope that it will be useful,                  */
+/*   but WITHOUT ANY WARRANTY; without even the implied warranty of         */
+/*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
+/*   GNU Lesser General Public License for more details.                    */
+/*                                                                          */
+/*   See the GNU Lesser General Public License version 2.1                  */
+/*   for more details (enclosed in the file licenses/LGPLv2.1).             */
+/*                                                                          */
+/* ************************************************************************ */
+
+/* --- Generated Frama-C Server API --- */
+
+/**
+   Region Analysis
+   @packageDocumentation
+   @module frama-c/plugins/region/api
+*/
+
+//@ts-ignore
+import * as Json from 'dome/data/json';
+//@ts-ignore
+import * as Compare from 'dome/data/compare';
+//@ts-ignore
+import * as Server from 'frama-c/server';
+//@ts-ignore
+import * as State from 'frama-c/states';
+
+//@ts-ignore
+import { byDecl } from 'frama-c/kernel/api/ast';
+//@ts-ignore
+import { byMarker } from 'frama-c/kernel/api/ast';
+//@ts-ignore
+import { decl } from 'frama-c/kernel/api/ast';
+//@ts-ignore
+import { declDefault } from 'frama-c/kernel/api/ast';
+//@ts-ignore
+import { jDecl } from 'frama-c/kernel/api/ast';
+//@ts-ignore
+import { jMarker } from 'frama-c/kernel/api/ast';
+//@ts-ignore
+import { marker } from 'frama-c/kernel/api/ast';
+//@ts-ignore
+import { markerDefault } from 'frama-c/kernel/api/ast';
+
+export type node = Json.index<'#node'>;
+
+/** Decoder for `node` */
+export const jNode: Json.Decoder<node> = Json.jIndex<'#node'>('#node');
+
+/** Natural order for `node` */
+export const byNode: Compare.Order<node> = Compare.number;
+
+/** Default value for `node` */
+export const nodeDefault: node = Json.jIndex<'#node'>('#node')(-1);
+
+export type range =
+  { offset: number, length: number, cells: number, data: node };
+
+/** Decoder for `range` */
+export const jRange: Json.Decoder<range> =
+  Json.jObject({
+    offset: Json.jNumber,
+    length: Json.jNumber,
+    cells: Json.jNumber,
+    data: jNode,
+  });
+
+/** Natural order for `range` */
+export const byRange: Compare.Order<range> =
+  Compare.byFields
+    <{ offset: number, length: number, cells: number, data: node }>({
+    offset: Compare.number,
+    length: Compare.number,
+    cells: Compare.number,
+    data: byNode,
+  });
+
+/** Default value for `range` */
+export const rangeDefault: range =
+  { offset: 0, length: 0, cells: 0, data: nodeDefault };
+
+export type region =
+  { node: node, roots: string[], labels: string[], parents: node[],
+    sizeof: number, ranges: range[], pointed?: node, reads: boolean,
+    writes: boolean, bytes: boolean, label: string, title: string };
+
+/** Decoder for `region` */
+export const jRegion: Json.Decoder<region> =
+  Json.jObject({
+    node: jNode,
+    roots: Json.jArray(Json.jString),
+    labels: Json.jArray(Json.jString),
+    parents: Json.jArray(jNode),
+    sizeof: Json.jNumber,
+    ranges: Json.jArray(jRange),
+    pointed: Json.jOption(jNode),
+    reads: Json.jBoolean,
+    writes: Json.jBoolean,
+    bytes: Json.jBoolean,
+    label: Json.jString,
+    title: Json.jString,
+  });
+
+/** Natural order for `region` */
+export const byRegion: Compare.Order<region> =
+  Compare.byFields
+    <{ node: node, roots: string[], labels: string[], parents: node[],
+       sizeof: number, ranges: range[], pointed?: node, reads: boolean,
+       writes: boolean, bytes: boolean, label: string, title: string }>({
+    node: byNode,
+    roots: Compare.array(Compare.alpha),
+    labels: Compare.array(Compare.alpha),
+    parents: Compare.array(byNode),
+    sizeof: Compare.number,
+    ranges: Compare.array(byRange),
+    pointed: Compare.defined(byNode),
+    reads: Compare.boolean,
+    writes: Compare.boolean,
+    bytes: Compare.boolean,
+    label: Compare.string,
+    title: Compare.string,
+  });
+
+/** Default value for `region` */
+export const regionDefault: region =
+  { node: nodeDefault, roots: [], labels: [], parents: [], sizeof: 0,
+    ranges: [], pointed: undefined, reads: false, writes: false,
+    bytes: false, label: '', title: '' };
+
+/** Region Analysis Updated */
+export const updated: Server.Signal = {
+  name: 'plugins.region.updated',
+};
+
+const compute_internal: Server.ExecRequest<decl,null> = {
+  kind: Server.RqKind.EXEC,
+  name: 'plugins.region.compute',
+  input: jDecl,
+  output: Json.jNull,
+  fallback: null,
+  signals: [],
+};
+/** Compute regions for the given declaration */
+export const compute: Server.ExecRequest<decl,null>= compute_internal;
+
+const regions_internal: Server.GetRequest<decl,region[]> = {
+  kind: Server.RqKind.GET,
+  name: 'plugins.region.regions',
+  input: jDecl,
+  output: Json.jArray(jRegion),
+  fallback: [],
+  signals: [ { name: 'plugins.region.updated' } ],
+};
+/** Returns computed regions for the given declaration */
+export const regions: Server.GetRequest<decl,region[]>= regions_internal;
+
+const regionsAt_internal: Server.GetRequest<[ marker, boolean ],region[]> = {
+  kind: Server.RqKind.GET,
+  name: 'plugins.region.regionsAt',
+  input: Json.jPair( jMarker, Json.jBoolean,),
+  output: Json.jArray(jRegion),
+  fallback: [],
+  signals: [ { name: 'plugins.region.updated' } ],
+};
+/** Compute regions at the given marker */
+export const regionsAt: Server.GetRequest<[ marker, boolean ],region[]>= regionsAt_internal;
+
+const localize_internal: Server.GetRequest<
+  [ marker, boolean ],
+  node |
+  undefined
+  > = {
+  kind: Server.RqKind.GET,
+  name: 'plugins.region.localize',
+  input: Json.jPair( jMarker, Json.jBoolean,),
+  output: Json.jOption(jNode),
+  fallback: undefined,
+  signals: [ { name: 'plugins.region.updated' } ],
+};
+/** Localize in the local (true) or global map (false) */
+export const localize: Server.GetRequest<
+  [ marker, boolean ],
+  node |
+  undefined
+  >= localize_internal;
+
+/* ------------------------------------- */
diff --git a/ivette/src/frama-c/plugins/region/index.tsx b/ivette/src/frama-c/plugins/region/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..eed8bea5b81bb105826aaab9a9c1e05b85593f5a
--- /dev/null
+++ b/ivette/src/frama-c/plugins/region/index.tsx
@@ -0,0 +1,99 @@
+/* ************************************************************************ */
+/*                                                                          */
+/*   This file is part of Frama-C.                                          */
+/*                                                                          */
+/*   Copyright (C) 2007-2024                                                */
+/*     CEA (Commissariat à l'énergie atomique et aux énergies               */
+/*          alternatives)                                                   */
+/*                                                                          */
+/*   you can redistribute it and/or modify it under the terms of the GNU    */
+/*   Lesser General Public License as published by the Free Software        */
+/*   Foundation, version 2.1.                                               */
+/*                                                                          */
+/*   It is distributed in the hope that it will be useful,                  */
+/*   but WITHOUT ANY WARRANTY; without even the implied warranty of         */
+/*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
+/*   GNU Lesser General Public License for more details.                    */
+/*                                                                          */
+/*   See the GNU Lesser General Public License version 2.1                  */
+/*   for more details (enclosed in the file licenses/LGPLv2.1).             */
+/*                                                                          */
+/* ************************************************************************ */
+
+// --------------------------------------------------------------------------
+// --- Regions
+// --------------------------------------------------------------------------
+
+import React from 'react';
+import { Label } from 'dome/controls/labels';
+import { LCD } from 'dome/controls/displays';
+import { IconButton } from 'dome/controls/buttons';
+import * as Dome from 'dome';
+import * as Tools from 'dome/frame/toolbars';
+import * as Ivette from 'ivette';
+import * as Server from 'frama-c/server';
+import * as States from 'frama-c/states';
+import * as Region from './api';
+import { MemoryView } from './memory';
+import './style.css';
+
+function RegionAnalys(): JSX.Element {
+  const [kf, setKf] = React.useState<States.Scope>();
+  const [kfName, setName] = React.useState<string>();
+  const [pinned, setPinned] = React.useState(false);
+  const [running, setRunning] = React.useState(false);
+  const setComputing = Dome.useProtected(setRunning);
+  const scope = States.useCurrentScope();
+  const { kind, name } = States.useDeclaration(scope);
+  const regions = States.useRequestStable(Region.regions, kf);
+  React.useEffect(() => {
+    if (!pinned && kind === 'FUNCTION' && scope !== kf) {
+      setKf(scope);
+      setName(name);
+    } else if (!Server.isRunning()) {
+      setKf(undefined);
+      setName(undefined);
+      setPinned(false);
+    }
+  }, [pinned, kind, name, scope, kf]);
+  async function compute(): Promise<void> {
+    try {
+      setComputing(true);
+      await Server.send(Region.compute, kf);
+    } finally {
+      setComputing(false);
+    }
+  }
+  return (
+    <>
+      <Tools.ToolBar>
+        <Label label='Function' />
+        <LCD className='region-lcd' label={kfName ?? '---'} />
+        <Tools.Button
+          icon={running ? 'EXECUTE' : 'MEDIA.PLAY'}
+          title='Run region analysis on the selected function'
+          disabled={running}
+          visible={kf !== undefined && regions.length === 0}
+          onClick={compute}
+        />
+        <IconButton
+          icon='PIN'
+          display={kf !== undefined}
+          title='Keep focus on current function'
+          selected={pinned}
+          onClick={() => setPinned(!pinned)}
+        />
+      </Tools.ToolBar>
+      <MemoryView regions={regions} />
+    </>
+  );
+}
+
+Ivette.registerComponent({
+  id: 'fc.region.main',
+  label: 'Region Analysis',
+  preferredPosition: 'BD',
+  children: <RegionAnalys />,
+});
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/frama-c/plugins/region/memory.tsx b/ivette/src/frama-c/plugins/region/memory.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..a59a4adac95021d3e17cd48d6e10cb0e9aceb15f
--- /dev/null
+++ b/ivette/src/frama-c/plugins/region/memory.tsx
@@ -0,0 +1,124 @@
+/* ************************************************************************ */
+/*                                                                          */
+/*   This file is part of Frama-C.                                          */
+/*                                                                          */
+/*   Copyright (C) 2007-2024                                                */
+/*     CEA (Commissariat à l'énergie atomique et aux énergies               */
+/*          alternatives)                                                   */
+/*                                                                          */
+/*   you can redistribute it and/or modify it under the terms of the GNU    */
+/*   Lesser General Public License as published by the Free Software        */
+/*   Foundation, version 2.1.                                               */
+/*                                                                          */
+/*   It is distributed in the hope that it will be useful,                  */
+/*   but WITHOUT ANY WARRANTY; without even the implied warranty of         */
+/*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
+/*   GNU Lesser General Public License for more details.                    */
+/*                                                                          */
+/*   See the GNU Lesser General Public License version 2.1                  */
+/*   for more details (enclosed in the file licenses/LGPLv2.1).             */
+/*                                                                          */
+/* ************************************************************************ */
+
+// --------------------------------------------------------------------------
+// --- Regions
+// --------------------------------------------------------------------------
+
+import React from 'react';
+import * as Dot from 'dome/graph/diagram';
+import * as Region from './api';
+
+function makeRecord(
+  edges: Dot.Edge[],
+  source: string,
+  sizeof: number,
+  ranges: Region.range[]
+): Dot.Cell[] {
+  if (ranges.length === 0) return [];
+  const cells: Dot.Cell[] = [];
+  let offset = 0;
+  ranges.forEach((rg, i) => {
+    const port = `r${i}`;
+    const target = `n${rg.data}`;
+    edges.push({ source, sourcePort: port, target });
+    if (offset !== rg.offset)
+      cells.push(`${offset}..${rg.offset - 1} ##`);
+    offset = rg.offset + rg.length;
+    cells.push({
+      label: `${rg.offset}..${offset - 1} [${rg.cells}]`,
+      port,
+    });
+  });
+  if (offset !== sizeof)
+    cells.push(`${offset}..${sizeof - 1} ##`);
+  return cells;
+}
+
+interface Diagram {
+  nodes: Dot.Node[];
+  edges: Dot.Edge[];
+}
+
+function makeDiagram(regions: readonly Region.region[]): Diagram {
+  const nodes: Dot.Node[] = [];
+  const edges: Dot.Edge[] = [];
+  regions.forEach(r => {
+    const id = `n${r.node}`;
+    // --- Color
+    const color =
+      r.bytes ? 'red' :
+        r.pointed !== undefined
+          ? (r.writes ? 'orange' : 'yellow')
+          : (r.writes && r.reads) ? 'green' :
+            r.writes ? 'pink' : r.reads ? 'grey' : 'white';
+    // --- Shape
+    const font = r.ranges.length > 0 ? 'mono' : 'sans';
+    const cells = makeRecord(edges, id, r.sizeof, r.ranges);
+    const shape = cells.length > 0 ? cells : undefined;
+    nodes.push({ id, font, color, label: r.label, title: r.title, shape });
+    // --- Labels
+    const L: Dot.Node =
+      { id: '', shape: 'note', font: 'mono' };
+    r.labels.forEach(a => {
+      const lid = `L${a}`;
+      nodes.push({ ...L, id: lid, label: `${a}:` });
+      edges.push({ source: lid, aligned: true, head: 'tee', target: id });
+    });
+    // --- Roots
+    const R: Dot.Node =
+      { id: '', shape: 'cds', font: 'mono' };
+    r.roots.forEach(x => {
+      const xid = `X${x}`;
+      nodes.push({ ...R, id: xid, label: x });
+      edges.push({ source: xid, headAnchor: "e", target: id });
+    });
+    // --- Pointed
+    if (r.pointed !== undefined) {
+      const pid = `n${r.pointed}`;
+      edges.push({ source: id, target: pid, head: 'dot', color: 'orange' });
+    }
+  });
+  return { nodes, edges };
+}
+
+function addSelected(d: Diagram, label: string, node: Region.node): void {
+  d.nodes.push({
+    id: 'Selected', label, title: "Selected Marker", shape: 'note'
+  });
+  d.edges.push({ source: 'Selected', target: `n${node}` });
+}
+
+export interface MemoryViewProps {
+  regions?: readonly Region.region[];
+  node?: Region.node | undefined;
+  label?: string;
+}
+
+export function MemoryView(props: MemoryViewProps): JSX.Element {
+  const { regions = [], label, node } = props;
+  const diagram = React.useMemo(() => makeDiagram(regions), [regions]);
+  if (label && node) addSelected(diagram, label, node);
+  return <Dot.Diagram {...diagram} />;
+}
+
+// --------------------------------------------------------------------------
diff --git a/ivette/src/frama-c/plugins/region/pkg.json b/ivette/src/frama-c/plugins/region/pkg.json
new file mode 100644
index 0000000000000000000000000000000000000000..f9ba9b34ba90149f99ddfff2cce84846d98bbf63
--- /dev/null
+++ b/ivette/src/frama-c/plugins/region/pkg.json
@@ -0,0 +1,3 @@
+{
+  "name": "Frama-C/Region"
+}
diff --git a/ivette/src/frama-c/plugins/region/style.css b/ivette/src/frama-c/plugins/region/style.css
new file mode 100644
index 0000000000000000000000000000000000000000..0076926a0527f0215a6fa22963d4129835df2279
--- /dev/null
+++ b/ivette/src/frama-c/plugins/region/style.css
@@ -0,0 +1,9 @@
+/* -------------------------------------------------------------------------- */
+/* --- CSS for Region Views                                               --- */
+/* -------------------------------------------------------------------------- */
+
+.region-lcd {
+    margin-top: 4px;
+    min-width: 160px;
+    text-align: center;
+}
diff --git a/nix/plugins-tests.nix b/nix/plugins-tests.nix
index a500d6d0ce11b44a18ed1f1690bd1ae250eb011a..b6a83abadd16fc420eb532347994f0e9646909e9 100644
--- a/nix/plugins-tests.nix
+++ b/nix/plugins-tests.nix
@@ -24,6 +24,7 @@ mk_tests {
       @src/plugins/markdown-report/tests/ptests \
       @src/plugins/nonterm/tests/ptests \
       @src/plugins/report/tests/ptests \
+      @src/plugins/region/tests/ptests \
       @src/plugins/server/tests/ptests \
       @src/plugins/variadic/tests/ptests
   '';
diff --git a/src/kernel_services/ast_queries/cil.ml b/src/kernel_services/ast_queries/cil.ml
index fd438e7de134a87f511ac4711e676947853f92ed..24d8d26e36fc42e6af21e74e0925beaf910a755c 100644
--- a/src/kernel_services/ast_queries/cil.ml
+++ b/src/kernel_services/ast_queries/cil.ml
@@ -5936,6 +5936,11 @@ let getCompField cinfo fieldName =
     (fun fi -> fi.fname = fieldName)
     (Option.value ~default:[] cinfo.cfields)
 
+let getCompType typ =
+  match unrollTypeSkel typ with
+  | TComp(comp,_) -> comp
+  | _ -> raise Not_found
+
 let sameSizeInt ?(machdep=false) (ik1 : ikind) (ik2 : ikind) =
   if machdep then bytesSizeOfInt ik1 == bytesSizeOfInt ik2
   else
diff --git a/src/kernel_services/ast_queries/cil.mli b/src/kernel_services/ast_queries/cil.mli
index 2e07fd682872d525bc099358fcd556ab91934839..83a9b53210a943b23d890b72c2337b6548c27b8b 100644
--- a/src/kernel_services/ast_queries/cil.mli
+++ b/src/kernel_services/ast_queries/cil.mli
@@ -658,6 +658,8 @@ val lenOfArray64: exp option -> Integer.t
 (** Return a named fieldinfo in compinfo, or raise Not_found *)
 val getCompField: compinfo -> string -> fieldinfo
 
+(** Return the compinfo of the typ, or raise Not_found *)
+val getCompType: typ -> compinfo
 
 (** A datatype to be used in conjunction with [existsType] *)
 type existsAction =
diff --git a/src/plugins/region/README.md b/src/plugins/region/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..02ef85649b845ce45b73275529c752a6e80524ae
--- /dev/null
+++ b/src/plugins/region/README.md
@@ -0,0 +1,58 @@
+# Region Analysis plugin (experimental)
+
+Region is a plugin for Frama-C that implements a new memory and alias analysis.
+This is a work in progress in early design stage.
+
+## Usage
+
+Regions are computed on demand from the registered Server requests.
+
+Command line option `-region` can be used to compute regions for all functions
+and dump on output the result analysis. With `-region-debug 1+` typed accesses
+are also reported.
+
+An API will be provided soon.
+
+## Annotations
+
+Behavior annotation `\region::region` and code annotation `\region::alias`
+can be used to specify supplementary aliases and to name regions.
+The syntax is the same:
+
+```
+//@ region [A:] lv, … , lv ;
+```
+
+Such a specification put all the l-values in the same region. A name can be
+given to the region, and reused later to put other l-values on the same region.
+Several regions can be specified in a single annotation.
+
+Unnamed regions from different annotations refer to _different_ regions unless
+they are aliased by sharing common l-values.
+
+L-values in region annotations use a similar syntax to ACSL terms.
+However, array access and pointer arithmetics must be specified with unlimited range, eg. `a[..]` or `*(p+(..))`.
+
+A syntax for specifying field aliasing and input/output array shapes will be provided soon.
+
+## Current Limitations
+
+Currently, ACSL contracts are not taken into account except for region
+annotations. Function calls are totally incomplete, and aliases that would be
+produces by function calls are not _yet_ taken into account. Use `//@alias` code-annotations as a temporary work around.
+
+## Ivette Support
+
+The Region Analysis component can be used to compute and visualize regions.
+Tooltips print access types to each regions.
+
+Colored regions means the following:
+ - _Red_ regions are those accessed with incompatible types
+ - _Yellow_ regions are constant pointers
+ - _Orange_ regions are written pointers
+ - _Green_ regions are RW regions
+ - _Pink_ regions are written-only regions (might be drop)
+ - _Grey_ regions are read-only regions (constant, no memory)
+
+Regions access and localization of currently selected marker will be
+provided soon.
diff --git a/src/plugins/region/Region.ml b/src/plugins/region/Region.ml
new file mode 100644
index 0000000000000000000000000000000000000000..2b8fce64aec5b2f1ee6fe423ba7feb30eaf6ff69
--- /dev/null
+++ b/src/plugins/region/Region.ml
@@ -0,0 +1,25 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(* -------------------------------------------------------------------------- *)
+(* --- Region Analysis API                                                --- *)
+(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/region/Region.mli b/src/plugins/region/Region.mli
new file mode 100644
index 0000000000000000000000000000000000000000..d9615a6fffe2e821fdbe14da1d0eed73de2bfc9c
--- /dev/null
+++ b/src/plugins/region/Region.mli
@@ -0,0 +1,23 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(** Interface for the Region plug-in. *)
diff --git a/src/plugins/region/access.ml b/src/plugins/region/access.ml
new file mode 100644
index 0000000000000000000000000000000000000000..5baa3096b5d7d24b5bcb9d4dd089fef236646816
--- /dev/null
+++ b/src/plugins/region/access.ml
@@ -0,0 +1,83 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_types
+open Cil_datatype
+
+type acs =
+  | Exp of Stmt.t * exp
+  | Lval of Stmt.t * lval
+  | Init of Stmt.t * varinfo
+  | Term of Property.t * term_lval
+
+let compare (a : acs) (b : acs) : int =
+  match a, b with
+  | Init(sa,xa), Init(sb,xb) ->
+    let cmp = Stmt.compare sa sb in
+    if cmp <> 0 then cmp else Varinfo.compare xa xb
+  | Init _ , _ -> (-1)
+  | _ , Init _ -> (+1)
+
+  | Lval(sa,la), Lval(sb,lb) ->
+    let cmp = Stmt.compare sa sb in
+    if cmp <> 0 then cmp else Lval.compare la lb
+  | Lval _ , _ -> (-1)
+  | _ , Lval _ -> (+1)
+
+  | Exp(sa,ea), Exp(sb,eb) ->
+    let cmp = Stmt.compare sa sb in
+    if cmp <> 0 then cmp else Exp.compare ea eb
+  | Exp _ , _ -> (-1)
+  | _ , Exp _ -> (+1)
+
+  | Term(sa,ta), Term(sb,tb) ->
+    let cmp = Property.compare sa sb in
+    if cmp <> 0 then cmp else Term_lval.compare ta tb
+
+let pstmt fmt (s : stmt) =
+  match s.labels with
+  | Label(l,_,_)::_ -> Format.pp_print_string fmt l
+  | _ ->
+    let loc, _ = Stmt.loc s in
+    Format.fprintf fmt "L%d" loc.pos_lnum
+
+let pretty fmt = function
+  | Init(s,x) ->
+    Format.fprintf fmt "%a@%a" Varinfo.pretty x pstmt s
+  | Exp(s,e) ->
+    Format.fprintf fmt "%a@%a" Exp.pretty e pstmt s
+  | Lval(s,l) ->
+    Format.fprintf fmt "%a@%a" Lval.pretty l pstmt s
+  | Term(s,l) ->
+    Format.fprintf fmt "%a@%s" Term_lval.pretty l
+      (Property.Names.get_prop_name_id s)
+
+let typeof = function
+  | Init(_,x) -> x.vtype
+  | Lval(_,lv) -> Cil.typeOfLval lv
+  | Exp(_,e) -> Cil.typeOf e
+  | Term(_,lt) ->
+    match Cil.typeOfTermLval lt with
+    | Ctype ty -> ty
+    | _ -> Cil.voidType
+
+module Set = Set.Make(struct type t = acs let compare = compare end)
diff --git a/src/plugins/region/access.mli b/src/plugins/region/access.mli
new file mode 100644
index 0000000000000000000000000000000000000000..cadb24bc098a9b3908f67308bf3cd0bc39f1b7be
--- /dev/null
+++ b/src/plugins/region/access.mli
@@ -0,0 +1,36 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_types
+
+type acs =
+  | Exp of stmt * exp
+  | Lval of stmt * lval
+  | Init of stmt * varinfo
+  | Term of Property.t * term_lval
+
+val compare : acs -> acs -> int
+val pretty : Format.formatter -> acs -> unit
+
+val typeof : acs -> typ
+
+module Set : Set.S with type elt = acs
diff --git a/src/plugins/region/analysis.ml b/src/plugins/region/analysis.ml
new file mode 100644
index 0000000000000000000000000000000000000000..b64c6571bc83fbfa648159d1701a12bf6e597a33
--- /dev/null
+++ b/src/plugins/region/analysis.ml
@@ -0,0 +1,74 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_datatype
+
+(* -------------------------------------------------------------------------- *)
+(* ---  Projectification                                                  --- *)
+(* -------------------------------------------------------------------------- *)
+
+module DOMAIN : Datatype.S with type t = Code.domain =
+  Datatype.Make
+    (struct
+      type t = Code.domain
+      include Datatype.Undefined
+      let name = "Region.Analysis.MEMORY"
+      let mem_project = Datatype.never_any_project
+      let reprs =
+        let m = Memory.create () in
+        Code.[{ map = m; body = Stmt.Map.empty ; spec = Property.Map.empty }]
+    end)
+
+module STATE = State_builder.Hashtbl(Kernel_function.Hashtbl)(DOMAIN)
+    (struct
+      let size = 0
+      let name = "Region.Analysis.STATE"
+      let dependencies = [Ast.self]
+    end)
+
+(* -------------------------------------------------------------------------- *)
+(* ---  Memoized Access                                                   --- *)
+(* -------------------------------------------------------------------------- *)
+
+let find = STATE.find
+
+let get kf =
+  try STATE.find kf with Not_found ->
+    Options.feedback ~ontty:`Transient "Function %a…" Kernel_function.pretty kf ;
+    let domain = Code.domain kf in
+    STATE.add kf domain ;
+    Options.result "@[<v 2>Function %a:%t@]@."
+      Kernel_function.pretty kf
+      begin fun fmt ->
+        Memory.iter domain.map
+          begin fun r ->
+            Format.pp_print_newline fmt () ;
+            Memory.pp_region fmt r ;
+          end
+      end ;
+    domain
+
+let compute kf = ignore @@ get kf
+
+let add_hook f = STATE.add_hook_on_change (fun _ -> f())
+
+(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/region/analysis.mli b/src/plugins/region/analysis.mli
new file mode 100644
index 0000000000000000000000000000000000000000..e9279b47420bc40e87fef33265d8a9e86a15febc
--- /dev/null
+++ b/src/plugins/region/analysis.mli
@@ -0,0 +1,35 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_types
+
+(** @raises Not_found *)
+val find : kernel_function -> Code.domain
+
+(** Memoized *)
+val get : kernel_function -> Code.domain
+
+(** Memoized *)
+val compute : kernel_function -> unit
+
+(** Hook on update *)
+val add_hook : (unit -> unit) -> unit
diff --git a/src/plugins/region/annot.ml b/src/plugins/region/annot.ml
new file mode 100644
index 0000000000000000000000000000000000000000..825ebf5ce8f52c2fd19a66e55adf11ef979814a4
--- /dev/null
+++ b/src/plugins/region/annot.ml
@@ -0,0 +1,256 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Logic_ptree
+open Cil_types
+open Cil_datatype
+
+(* -------------------------------------------------------------------------- *)
+(* ---  Region Specifications                                             --- *)
+(* -------------------------------------------------------------------------- *)
+
+type path = {
+  loc : location ;
+  typ : typ ;
+  step: step ;
+}
+
+and step =
+  | Var of varinfo
+  | AddrOf of path
+  | Star of path
+  | Index of path
+  | Shift of path
+  | Field of path * fieldinfo
+  | Cast of typ * path
+
+type region = {
+  rname: string option ;
+  rpath: path list ;
+}
+
+(* -------------------------------------------------------------------------- *)
+(* ---  Printers                                                          --- *)
+(* -------------------------------------------------------------------------- *)
+
+let atomic = function
+  | Var _ | AddrOf _ | Star _ | Index _ | Field _ -> true
+  | Shift _ | Cast _ -> false
+
+let rec pp_step fmt = function
+  | Var x -> Varinfo.pretty fmt x
+  | Field(p,f) -> pfield p f fmt
+  | Index a -> Format.fprintf fmt "%a[..]" pp_atom a
+  | Shift a -> Format.fprintf fmt "%a+(..)" pp_atom a
+  | Star a -> Format.fprintf fmt "*%a" pp_atom a
+  | AddrOf a -> Format.fprintf fmt "&%a" pp_atom a
+  | Cast(t,a) -> Format.fprintf fmt "(%a)@,%a" Typ.pretty t pp_atom a
+
+and pfield p fd fmt =
+  match p.step with
+  | Star p -> Format.fprintf fmt "%a->%a" pp_atom p Fieldinfo.pretty fd
+  | _ -> Format.fprintf fmt "%a.%a" pp_atom p Fieldinfo.pretty fd
+
+and pp_atom fmt a =
+  if atomic a.step then pp_step fmt a.step
+  else Format.fprintf fmt "@[<hov 2>(%a)@]" pp_step a.step
+
+and pp_path fmt a = pp_step fmt a.step
+
+let pp_named fmt = function None -> () | Some a -> Format.fprintf fmt "%s: " a
+
+let pp_region fmt r =
+  match r.rpath with
+  | [] -> Format.pp_print_string fmt "\null"
+  | p::ps ->
+    begin
+      Format.fprintf fmt "@[<hov 2>" ;
+      pp_named fmt r.rname ;
+      pp_path fmt p ;
+      List.iter (Format.fprintf fmt ",@ %a" pp_path) ps ;
+      Format.fprintf fmt "@]" ;
+    end
+
+let pp_regions fmt = function
+  | [] -> Format.pp_print_string fmt "\null"
+  | r::rs ->
+    begin
+      Format.fprintf fmt "@[<hv 0>" ;
+      pp_region fmt r ;
+      List.iter (Format.fprintf fmt ",@ %a" pp_region) rs ;
+      Format.fprintf fmt "@]" ;
+    end
+
+(* -------------------------------------------------------------------------- *)
+(* ---  Parsers                                                           --- *)
+(* -------------------------------------------------------------------------- *)
+
+type env = {
+  context: Logic_typing.typing_context ;
+  mutable named: string option ;
+  mutable paths: path list ;
+  mutable specs: region list ;
+}
+
+let error (env:env) ~loc msg = env.context.error loc msg
+
+let parse_variable (env:env) ~loc x =
+  match env.context.find_var x with
+  | { lv_origin = Some v } -> { loc ; typ = v.vtype ; step = Var v }
+  | _ -> error env ~loc "Variable '%s' is not a C-variable" x
+
+let parse_field env ~loc comp f =
+  try Cil.getCompField comp f with Not_found ->
+    error env ~loc "No field '%s' in compound type '%s'" f comp.cname
+
+let parse_compinfo env ~loc typ =
+  try Cil.getCompType typ with Not_found ->
+    error env ~loc "Expected compound type for term"
+
+let parse_lrange (env: env) (e : lexpr) =
+  match e.lexpr_node with
+  | PLrange(None,None) -> ()
+  | _ ->
+    error env ~loc:e.lexpr_loc "Unexpected index (use unspecified range only)"
+
+let parse_typ env ~loc t =
+  let open Logic_typing in
+  let g = env.context in
+  let t = g.logic_type g loc g.pre_state t in
+  match Logic_utils.unroll_type t with
+  | Ctype typ -> typ
+  | _ -> error env ~loc "C-type expected for casting l-values"
+
+let rec parse_lpath (env:env) (e: lexpr) =
+  let loc = e.lexpr_loc in
+  match e.lexpr_node with
+  | PLvar x -> parse_variable env ~loc x
+  | PLunop( Ustar , p ) ->
+    let lv = parse_lpath env p in
+    if Cil.isPointerType lv.typ then
+      let te = Cil.typeOf_pointed lv.typ in
+      { loc ; step = Star lv ; typ = te }
+    else
+      error env ~loc "Pointer-type expected for operator '*'"
+  | PLunop( Uamp , p ) ->
+    let lv = parse_lpath env p in
+    let typ = TPtr( lv.typ , [] ) in
+    { loc ; step = AddrOf lv ; typ }
+  | PLbinop( p , Badd , rg ) ->
+    parse_lrange env rg ;
+    let { typ } as lv = parse_lpath env p in
+    if Cil.isPointerType typ then
+      { loc ; step = Shift lv ; typ = typ }
+    else
+    if Cil.isArrayType typ then
+      let te = Cil.typeOf_array_elem typ in
+      { loc ; step = Shift lv ; typ = TPtr(te,[]) }
+    else
+      error env ~loc "Pointer-type expected for operator '+'"
+  | PLdot( p , f ) ->
+    let lv = parse_lpath env p in
+    let comp = parse_compinfo env ~loc:lv.loc lv.typ in
+    let fd = parse_field env ~loc comp f in
+    { loc ; step = Field(lv,fd) ; typ = fd.ftype }
+  | PLarrow( p , f ) ->
+    let sp = { lexpr_loc = loc ; lexpr_node = PLunop(Ustar,p) } in
+    let pf = { lexpr_loc = loc ; lexpr_node = PLdot(sp,f) } in
+    parse_lpath env pf
+  | PLarrget( p , rg ) ->
+    parse_lrange env rg ;
+    let { typ } as lv = parse_lpath env p in
+    if Cil.isPointerType typ then
+      let pointed = Cil.typeOf_pointed typ in
+      let ls = { loc ; step = Shift lv ; typ } in
+      { loc ; step = Star ls ; typ = pointed }
+    else
+    if Cil.isArrayType typ then
+      let elt = Cil.typeOf_array_elem typ in
+      { loc ; step = Index lv ; typ = elt }
+    else
+      error env ~loc:lv.loc "Pointer or array type expected"
+  | PLcast( t , a ) ->
+    let lv = parse_lpath env a in
+    let ty = parse_typ env ~loc t in
+    { loc ; step = Cast(ty,lv) ; typ = ty }
+  | _ ->
+    error env ~loc "Unexpected expression for region spec"
+
+let rec parse_named_lpath (env:env) p =
+  match p.lexpr_node with
+  | PLnamed( name , p ) ->
+    if env.named <> None && env.paths <> [] then
+      begin
+        env.specs <- { rname = env.named ; rpath = env.paths } :: env.specs ;
+        env.paths <- [] ;
+      end ;
+    env.named <- Some name ;
+    parse_named_lpath env p
+  | _ ->
+    let path = parse_lpath env p in
+    env.paths <- path :: env.paths
+
+(* -------------------------------------------------------------------------- *)
+(* --- Spec Typechecking & Printing                                       --- *)
+(* -------------------------------------------------------------------------- *)
+
+let kspec = ref 0
+let registry = Hashtbl.create 0
+
+let of_extid id = try Hashtbl.find registry id with Not_found -> []
+let of_extension = function
+  | { ext_name="region" ; ext_kind = Ext_id k } -> of_extid k
+  | _ -> []
+let of_code_annot = function
+  | { annot_content = AExtended(_,_,e) } -> of_extension e
+  | _ -> []
+
+let of_behavior bhv = List.concat_map of_extension bhv.b_extended
+
+let typecheck typing_context _loc ps =
+  let env = {
+    named = None ;
+    context = typing_context ;
+    paths = [] ; specs = [] ;
+  } in
+  List.iter (parse_named_lpath env) ps ;
+  let id = !kspec in incr kspec ;
+  let specs = { rname = env.named ; rpath = env.paths } :: env.specs in
+  Hashtbl.add registry id @@ List.rev specs ;
+  Ext_id id
+
+let printer _pp fmt = function
+  | Ext_id k ->
+    let rs  = try Hashtbl.find registry k with Not_found -> [] in
+    pp_regions fmt rs
+  | _ -> ()
+
+let () =
+  begin
+    Acsl_extension.register_behavior
+      ~plugin:"region" "region" typecheck ~printer false ;
+    Acsl_extension.register_code_annot
+      ~plugin:"region" "alias" typecheck ~printer false ;
+  end
+
+
+(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/region/annot.mli b/src/plugins/region/annot.mli
new file mode 100644
index 0000000000000000000000000000000000000000..a2fd8bc531f698f97515c6d863fe5b3ba2f241ee
--- /dev/null
+++ b/src/plugins/region/annot.mli
@@ -0,0 +1,53 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_types
+
+type path = {
+  loc : location ;
+  typ : typ ;
+  step: step ;
+}
+
+and step =
+  | Var of varinfo
+  | AddrOf of path
+  | Star of path
+  | Index of path
+  | Shift of path
+  | Field of path * fieldinfo
+  | Cast of typ * path
+
+type region = {
+  rname: string option ;
+  rpath: path list ;
+}
+
+val pp_step : Format.formatter -> step -> unit
+val pp_atom : Format.formatter -> path -> unit
+val pp_path : Format.formatter -> path -> unit
+val pp_region : Format.formatter -> region -> unit
+val pp_regions : Format.formatter -> region list -> unit
+
+val of_extension : acsl_extension -> region list
+val of_code_annot : code_annotation -> region list
+val of_behavior : behavior -> region list
diff --git a/src/plugins/region/code.ml b/src/plugins/region/code.ml
new file mode 100644
index 0000000000000000000000000000000000000000..93bbc497df42f1f637b9fd2c72e4fcc29719c54a
--- /dev/null
+++ b/src/plugins/region/code.ml
@@ -0,0 +1,251 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_types
+open Cil_datatype
+open Memory
+
+(* -------------------------------------------------------------------------- *)
+(* ---  L-Values & Expressions                                            --- *)
+(* -------------------------------------------------------------------------- *)
+
+type value = { from : node option ; ptr : node option }
+let integral = { from = None ; ptr = None }
+let pointer m v =
+  match v.ptr with
+  | Some p -> v, p
+  | None ->
+    let p = add_cell m () in
+    Option.iter (fun s -> Memory.add_points_to m s p) v.from ;
+    { v with ptr = Some p }, p
+
+let rec add_lval (m:map) (s:stmt) (lv:lval) : node =
+  let h = fst lv in
+  add_loffset m s (add_lhost m s h) (Cil.typeOfLhost h) (snd lv)
+
+and add_lhost (m:map) (s:stmt) = function
+  | Var x -> Memory.add_root m x
+  | Mem e -> snd @@ pointer m @@ add_exp m s e
+
+and add_loffset (m:map) (s:stmt) (r:node) (ty:typ)= function
+  | NoOffset -> r
+  | Field(fd,ofs) ->
+    add_loffset m s (add_field m r fd) fd.ftype ofs
+  | Index(_,ofs) ->
+    add_loffset m s (add_index m r ty) (Cil.typeOf_array_elem ty) ofs
+
+and add_value m s e = ignore (add_exp m s e)
+
+and add_exp (m: map) (s:stmt) (e:exp) : value =
+  match e.enode with
+
+  | AddrOf lv | StartOf lv ->
+    let rv = Some (add_lval m s lv) in
+    { from = rv ; ptr = rv }
+
+  | Lval lv ->
+    let rv = add_lval m s lv in
+    Memory.read m rv (Lval(s,lv)) ;
+    let ptr = Memory.add_value m rv @@ Cil.typeOfLval lv in
+    { from = Some rv ; ptr }
+
+  | BinOp((PlusPI|MinusPI),p,k,_) ->
+    add_value m s k ;
+    let v = add_exp m s p in
+    Option.iter (fun r -> Memory.shift m r (Exp(s,e))) v.from ; v
+
+  | UnOp(_,e,_) ->
+    add_value m s e ; integral
+
+  | BinOp(_,a,b,_) ->
+    add_value m s a ; add_value m s b ; integral
+
+  | CastE(ty,p) ->
+    let v = add_exp m s p in
+    if Cil.isPointerType ty then
+      fst @@ pointer m @@ v
+    else
+      integral
+
+  | Const _
+  | SizeOf _ | SizeOfE _ | SizeOfStr _
+  | AlignOf _ | AlignOfE _
+    -> integral
+
+(* -------------------------------------------------------------------------- *)
+(* --- Initializers                                                       --- *)
+(* -------------------------------------------------------------------------- *)
+
+let rec add_init (m:map) (s:stmt) (acs:Access.acs) (lv:lval) (iv:init) =
+  match iv with
+
+  | SingleInit e ->
+    let r = add_lval m s lv in
+    Memory.write m r acs ;
+    Option.iter (Memory.add_points_to m r) (add_exp m s e).ptr
+
+  | CompoundInit(_,fvs) ->
+    List.iter
+      (fun (ofs,iv) ->
+         let lv = Cil.addOffsetLval ofs lv in
+         add_init m s acs lv iv
+      ) fvs
+
+(* -------------------------------------------------------------------------- *)
+(* --- Instructions                                                       --- *)
+(* -------------------------------------------------------------------------- *)
+
+let add_instr (m:map) (s:stmt) (instr:instr) =
+  match instr with
+  | Skip _ | Code_annot _ -> ()
+
+  | Set(lv,e,_) ->
+    let r = add_lval m s lv in
+    let v = add_exp m s e in
+    Memory.write m r (Lval(s,lv)) ;
+    Option.iter (Memory.add_points_to m r) v.ptr
+
+  | Local_init(x,AssignInit iv,_) ->
+    let acs = Access.Init(s,x) in
+    add_init m s acs (Var x,NoOffset) iv
+
+  | Call(lr,e,es,_) ->
+    add_value m s e ;
+    List.iter (add_value m s) es ;
+    Option.iter
+      (fun lv ->
+         let r = add_lval m s lv in
+         Memory.write m r (Lval(s,lv))
+      ) lr ;
+    Options.warning ~source:(fst @@ Stmt.loc s) "Incomplete call analysis"
+
+  | Local_init(_,ConsInit _,_) ->
+    Options.warning ~source:(fst @@ Stmt.loc s)
+      "Constructor init not yet implemented"
+  | Asm _ ->
+    Options.warning ~source:(fst @@ Stmt.loc s)
+      "Inline assembly not supported (ignored)"
+
+(* -------------------------------------------------------------------------- *)
+(* --- Statements                                                         --- *)
+(* -------------------------------------------------------------------------- *)
+
+type rmap = Memory.map Stmt.Map.t ref
+
+let store rmap m s =
+  rmap := Stmt.Map.add s (Memory.copy ~locked:true m) !rmap
+
+let rec add_stmt (r:rmap) (m:map) (s:stmt) =
+  let annots = Annotations.code_annot s in
+  if annots <> [] then
+    Options.warning ~source:(fst @@ Stmt.loc s)
+      "Annotations not analyzed" ;
+  match s.skind with
+  | Instr ki -> add_instr m s ki ; store r m s
+  | Return(Some e,_) -> add_value m s e ; store r m s
+  | Goto _ | Break _ | Continue _ | Return(None,_) -> store r m s
+  | If(e,st,se,_) ->
+    add_value m s e ;
+    store r m s ;
+    add_block r m st ;
+    add_block r m se ;
+  | Switch(e,b,_,_) ->
+    add_value  m s e ;
+    store r m s ;
+    add_block r m b ;
+  | Block b | Loop(_,b,_,_,_) -> add_block r m b
+  | UnspecifiedSequence s -> add_block r m @@ Cil.block_from_unspecified_sequence s
+  | Throw(exn,_) -> Option.iter (fun (e,_) -> add_value  m s e) exn
+  | TryCatch(b,hs,_)  ->
+    add_block r m b ;
+    List.iter (fun (c,b) -> add_catch r m c ; add_block r m b) hs ;
+  | TryExcept(a,(ks,e),b,_) ->
+    add_block r m a ;
+    List.iter (add_instr m s) ks ;
+    add_value  m s e ;
+    add_block r m b ;
+  | TryFinally(a,b,_) ->
+    add_block r m a ;
+    add_block r m b ;
+
+and add_catch (r:rmap) (m:map) (c:catch_binder) =
+  match c with
+  | Catch_all -> ()
+  | Catch_exn(_,xbs) -> List.iter (fun (_,b) -> add_block r m b) xbs
+
+and add_block (r:rmap) (m:map) (b:block) =
+  List.iter (add_stmt r m) b.bstmts
+
+(* -------------------------------------------------------------------------- *)
+(* --- Behavior                                                           --- *)
+(* -------------------------------------------------------------------------- *)
+
+type imap = Memory.map Property.Map.t ref
+
+let istore imap m ip =
+  imap := Property.Map.add ip (Memory.copy ~locked:true m) !imap
+
+let add_bhv ~kf (s:imap) (m:map) (bhv:behavior) =
+  List.iter
+    (fun e ->
+       let rs = Annot.of_extension e in
+       if rs <> [] then
+         begin
+           List.iter (Logic.add_region m) rs ;
+           let ip = Property.ip_of_extended (ELContract kf) e in
+           istore s m ip ;
+         end
+    ) bhv.b_extended
+
+(* -------------------------------------------------------------------------- *)
+(* --- Function                                                           --- *)
+(* -------------------------------------------------------------------------- *)
+
+type domain = {
+  map : map ;
+  body : map Stmt.Map.t ;
+  spec : map Property.Map.t ;
+}
+
+let domain ?global kf =
+  let m = match global with Some g -> g | None -> Memory.create () in
+  let r = ref Stmt.Map.empty in
+  let s = ref Property.Map.empty in
+  begin
+    try
+      let funspec = Annotations.funspec kf in
+      List.iter (add_bhv ~kf s m) funspec.spec_behavior ;
+    with Annotations.No_funspec _ -> ()
+  end ;
+  begin
+    try
+      let fundec = Kernel_function.get_definition kf in
+      add_block r m fundec.sbody ;
+    with Kernel_function.No_Definition -> ()
+  end ;
+  {
+    map = Memory.copy ~locked:true m ;
+    body = !r ;
+    spec = !s ;
+  }
+
+(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/region/code.mli b/src/plugins/region/code.mli
new file mode 100644
index 0000000000000000000000000000000000000000..46cea3a9d8da0e6fc089aab9755498c699a2b022
--- /dev/null
+++ b/src/plugins/region/code.mli
@@ -0,0 +1,35 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_types
+open Cil_datatype
+open Memory
+
+(** All the provided maps are locked. *)
+type domain = {
+  map : map ;
+  body : map Stmt.Map.t ;
+  spec : map Property.Map.t ;
+}
+
+(** The global map, if provided, is used as an accumulator. *)
+val domain : ?global:map -> kernel_function -> domain
diff --git a/src/plugins/region/dune b/src/plugins/region/dune
new file mode 100644
index 0000000000000000000000000000000000000000..f4f00f77e3abbaac66d5fb72b9b59018d629a236
--- /dev/null
+++ b/src/plugins/region/dune
@@ -0,0 +1,49 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                                                                        ;;
+;;  This file is part of Frama-C.                                         ;;
+;;                                                                        ;;
+;;  Copyright (C) 2007-2024                                               ;;
+;;    CEA (Commissariat à l'énergie atomique et aux énergies              ;;
+;;         alternatives)                                                  ;;
+;;                                                                        ;;
+;;  you can redistribute it and/or modify it under the terms of the GNU   ;;
+;;  Lesser General Public License as published by the Free Software       ;;
+;;  Foundation, version 2.1.                                              ;;
+;;                                                                        ;;
+;;  It is distributed in the hope that it will be useful,                 ;;
+;;  but WITHOUT ANY WARRANTY; without even the implied warranty of        ;;
+;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         ;;
+;;  GNU Lesser General Public License for more details.                   ;;
+;;                                                                        ;;
+;;  See the GNU Lesser General Public License version 2.1                 ;;
+;;  for more details (enclosed in the file licenses/LGPLv2.1).            ;;
+;;                                                                        ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(rule
+ (alias frama-c-configure)
+ (deps (universe))
+ (action
+  (progn
+   (echo "Region:" %{lib-available:frama-c-region.core} "\n")
+   (echo "  - Server:" %{lib-available:frama-c-server.core} "\n")
+   (echo "  - UnionFind:" %{lib-available:unionFind} "\n"))))
+
+(env
+ (release
+  (flags -noassert)))
+
+(library
+ (name Region)
+ (public_name frama-c-region.core)
+ (flags -open Frama_c_kernel :standard -w -9)
+ (libraries frama-c.kernel frama-c-server.core unionFind)
+ (instrumentation (backend landmarks))
+ (instrumentation (backend bisect_ppx)))
+
+(plugin
+ (optional)
+ (name region)
+ (libraries frama-c-region.core)
+ (site
+  (frama-c plugins)))
diff --git a/src/plugins/region/dune-project b/src/plugins/region/dune-project
new file mode 100644
index 0000000000000000000000000000000000000000..cbfcc90d4ec4c33141700984567ea75170a167cb
--- /dev/null
+++ b/src/plugins/region/dune-project
@@ -0,0 +1,25 @@
+(lang dune 3.7)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;                                                                        ;;
+;;  This file is part of Frama-C.                                         ;;
+;;                                                                        ;;
+;;  Copyright (C) 2007-2024                                               ;;
+;;    CEA (Commissariat à l'énergie atomique et aux énergies              ;;
+;;         alternatives)                                                  ;;
+;;                                                                        ;;
+;;  you can redistribute it and/or modify it under the terms of the GNU   ;;
+;;  Lesser General Public License as published by the Free Software       ;;
+;;  Foundation, version 2.1.                                              ;;
+;;                                                                        ;;
+;;  It is distributed in the hope that it will be useful,                 ;;
+;;  but WITHOUT ANY WARRANTY; without even the implied warranty of        ;;
+;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         ;;
+;;  GNU Lesser General Public License for more details.                   ;;
+;;                                                                        ;;
+;;  See the GNU Lesser General Public License version 2.1                 ;;
+;;  for more details (enclosed in the file licenses/LGPLv2.1).            ;;
+;;                                                                        ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(name frama-c-region)
+(using dune_site 0.1)
diff --git a/src/plugins/region/frama-c-region.opam b/src/plugins/region/frama-c-region.opam
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/plugins/region/logic.ml b/src/plugins/region/logic.ml
new file mode 100644
index 0000000000000000000000000000000000000000..6e68710a47566de2ab8c01fb50d348c2365e3707
--- /dev/null
+++ b/src/plugins/region/logic.ml
@@ -0,0 +1,54 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Annot
+open Memory
+
+let rec add_lval (m:map) (p:path): node =
+  match p.step with
+  | Var x -> add_root m x
+  | Field(lv,fd) -> Memory.add_field m (add_lval m lv) fd
+  | Index lv -> Memory.add_index m (add_lval m lv) lv.typ
+  | Star e | Cast(_,e) -> add_pointer m e
+  | Shift _ | AddrOf _ ->
+    Options.error ~source:(fst p.loc)
+      "Unexpected expression (l-value expected)" ;
+    Memory.add_cell m ()
+and add_pointer  (m:map) (p:path): Memory.node =
+  match add_exp m p with
+  | None -> add_cell m ()
+  | Some r -> r
+
+and add_exp (m:map) (p:path): Memory.node option =
+  match p.step with
+  | (Var _ | Field _ | Index _ | Star _ | Cast _) ->
+    let r = add_lval m p in
+    add_value m r p.typ
+  | AddrOf p -> Some (add_lval m p)
+  | Shift p -> add_exp m p
+
+let add_region (m: map) (r : Annot.region) =
+  let rs = List.map (add_lval m) r.rpath in
+  merge_all m @@
+  match r.rname with
+  | None -> rs
+  | Some a -> add_label m a :: rs
diff --git a/src/plugins/region/logic.mli b/src/plugins/region/logic.mli
new file mode 100644
index 0000000000000000000000000000000000000000..e5fb991d29aa65367eec51046aa0e15c3b86e6cf
--- /dev/null
+++ b/src/plugins/region/logic.mli
@@ -0,0 +1,27 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Memory
+
+val add_lval : map -> Annot.path -> node
+val add_exp : map -> Annot.path -> node option
+val add_region : map -> Annot.region -> unit
diff --git a/src/plugins/region/memory.ml b/src/plugins/region/memory.ml
new file mode 100644
index 0000000000000000000000000000000000000000..ac29123b225b9979e8d3ae8f8da320ce756307a4
--- /dev/null
+++ b/src/plugins/region/memory.ml
@@ -0,0 +1,513 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_types
+open Cil_datatype
+module Ufind = UnionFind.Make(Store)
+module Vmap = Varinfo.Map
+module Vset = Varinfo.Set
+module Lmap = Map.Make(String)
+module Lset = Set.Make(String)
+
+(* -------------------------------------------------------------------------- *)
+(* --- Region Maps                                                        --- *)
+(* -------------------------------------------------------------------------- *)
+
+(* All offsets in bits *)
+
+type node = chunk Ufind.rref
+
+and layout =
+  | Blob
+  | Cell of int * node option
+  | Compound of int * node Ranges.t
+
+and chunk = {
+  cparents: node list ;
+  croots: Vset.t ;
+  clabels: Lset.t ;
+  creads: Access.Set.t ;
+  cwrites: Access.Set.t ;
+  cshifts: Access.Set.t ;
+  clayout: layout ;
+}
+
+type rg = node Ranges.range
+
+type map = {
+  store: chunk Ufind.store ;
+  mutable locked: bool ;
+  mutable roots: node Vmap.t ;
+  mutable labels: node Lmap.t ;
+}
+
+(* -------------------------------------------------------------------------- *)
+(* --- Accessors                                                          --- *)
+(* -------------------------------------------------------------------------- *)
+
+let sizeof = function Blob -> 0 | Cell(s,_) | Compound(s,_) -> s
+let ranges = function Blob | Cell _ -> [] | Compound(_,R rs) -> rs
+let pointed = function Blob | Compound _ -> None | Cell(_,p) -> p
+
+let types (m : chunk) : typ list =
+  let pool = ref Typ.Set.empty in
+  let add acs =
+    pool := Typ.Set.add (Cil.unrollType @@ Access.typeof acs) !pool in
+  Access.Set.iter add m.creads ;
+  Access.Set.iter add m.cwrites ;
+  Typ.Set.elements !pool
+
+let failwith_locked m fn =
+  if m.locked then raise (Invalid_argument fn)
+
+let lock m = m.locked <- true
+let unlock m = m.locked <- false
+
+(* -------------------------------------------------------------------------- *)
+(* --- Printers                                                           --- *)
+(* -------------------------------------------------------------------------- *)
+
+let pp_node fmt (n : node) = Format.fprintf fmt "R%04x" @@ Store.id n
+
+let pp_layout fmt = function
+  | Blob -> Format.pp_print_string fmt "<blob>"
+  | Cell(s,None) -> Format.fprintf fmt "<%04d>" s
+  | Cell(s,Some n) -> Format.fprintf fmt "<%04d>(*%a)" s pp_node n
+  | Compound(s,rg) ->
+    Format.fprintf fmt "@[<hv 0>{%04d" s ;
+    Ranges.iteri
+      (fun (rg : rg) ->
+         Format.fprintf fmt "@ | %a: %a" Ranges.pp_range rg pp_node rg.data
+      ) rg ;
+    Format.fprintf fmt "@ }@]"
+
+let pp_chunk fmt (n: node) (m: chunk) =
+  begin
+    let acs r s = if Access.Set.is_empty s then '-' else r in
+    Format.fprintf fmt "@[<hov 2>%a: %c%c%c" pp_node n
+      (acs 'R' m.creads) (acs 'W' m.cwrites) (acs 'A' m.cshifts) ;
+    List.iter (Format.fprintf fmt "@ (%a)" Typ.pretty) (types m) ;
+    Lset.iter (Format.fprintf fmt "@ %s:") m.clabels ;
+    Vset.iter (Format.fprintf fmt "@ %a" Varinfo.pretty) m.croots ;
+    if Options.debug_atleast 1 then
+      begin
+        Access.Set.iter (Format.fprintf fmt "@ R:%a" Access.pretty) m.creads ;
+        Access.Set.iter (Format.fprintf fmt "@ W:%a" Access.pretty) m.cwrites ;
+        Access.Set.iter (Format.fprintf fmt "@ A:%a" Access.pretty) m.cshifts ;
+      end ;
+    Format.fprintf fmt "@ %a ;@]" pp_layout m.clayout ;
+  end
+[@@ warning "-32"]
+
+(* -------------------------------------------------------------------------- *)
+(* --- Map Constructors                                                   --- *)
+(* -------------------------------------------------------------------------- *)
+
+let create () = {
+  locked = false ;
+  store = Ufind.new_store () ;
+  roots = Vmap.empty ;
+  labels = Lmap.empty ;
+}
+
+let copy ?locked m = {
+  locked = (match locked with None -> m.locked | Some l -> l) ;
+  store = Ufind.copy m.store ;
+  roots = m.roots ;
+  labels = m.labels ;
+}
+
+let empty = {
+  cparents = [] ;
+  croots = Vset.empty ;
+  clabels = Lset.empty ;
+  creads = Access.Set.empty ;
+  cwrites = Access.Set.empty ;
+  cshifts = Access.Set.empty ;
+  clayout = Blob ;
+}
+
+(* -------------------------------------------------------------------------- *)
+(* --- Map                                                                --- *)
+(* -------------------------------------------------------------------------- *)
+
+let id = Store.id
+let forge = Store.forge
+
+let node map node =
+  try Ufind.find map.store node
+  with Not_found -> node
+
+let nodes map ns = Store.list @@ List.map (node map) ns
+
+let get map node =
+  try Ufind.get map.store node
+  with Not_found -> empty
+
+(* -------------------------------------------------------------------------- *)
+(* --- Chunk Constructors                                                 --- *)
+(* -------------------------------------------------------------------------- *)
+
+let add_cell (m: map) ?size ?ptr ?root ?label () =
+  failwith_locked m "Region.Memory.add_cell" ;
+  let clayout = match size, ptr with
+    | None, None -> Blob
+    | None, Some _ -> Cell(Cil.bitsSizeOf Cil.voidPtrType,ptr)
+    | Some s, _ -> Cell(s,ptr) in
+  let croots = Vset.(match root with None -> empty | Some v -> singleton v) in
+  let clabels = Lset.(match label with None -> empty | Some a -> singleton a) in
+  Ufind.make m.store { empty with clayout ; croots ; clabels }
+
+let update (m: map) (n: node) (f: chunk -> chunk) =
+  let r = get m n in
+  Ufind.set m.store n (f r)
+
+let add_range (m: map) ~size ~offset ~length ~data : node =
+  failwith_locked m "Region.Memory.add_range" ;
+  let last = offset + length in
+  if not (0 <= offset && offset < last && last <= size) then
+    raise (Invalid_argument "Region.Memory.add_range") ;
+  let clayout = Compound(size, Ranges.singleton { offset ; length ; data }) in
+  let n = Ufind.make m.store { empty with clayout } in
+  update m data (fun r -> { r with cparents = nodes m @@ n :: r.cparents }) ; n
+
+let add_root (m: map) v =
+  try Vmap.find v m.roots with Not_found ->
+    failwith_locked m "Region.Memory.add_root" ;
+    let n = add_cell m ~root:v () in
+    m.roots <- Vmap.add v n m.roots ; n
+
+let add_label (m: map) a =
+  try Lmap.find a m.labels with Not_found ->
+    failwith_locked m "Region.Memory.add_label" ;
+    let n = add_cell m ~label:a () in
+    m.labels <- Lmap.add a n m.labels ; n
+
+(* -------------------------------------------------------------------------- *)
+(* --- Iterator                                                           --- *)
+(* -------------------------------------------------------------------------- *)
+
+type range = {
+  offset: int ;
+  length: int ;
+  cells: int ;
+  data: node ;
+}
+
+type region = {
+  node: node ;
+  parents: node list ;
+  roots: varinfo list ;
+  labels: string list ;
+  types: typ list ;
+  reads: Access.acs list ;
+  writes: Access.acs list ;
+  shifts: Access.acs list ;
+  sizeof: int ;
+  ranges: range list ;
+  pointed: node option ;
+}
+
+let pp_range fmt (r: range) =
+  Format.fprintf fmt "%d..%d [%d]: %a"
+    r.offset (r.offset + r.length) r.cells pp_node r.data
+
+let pp_region fmt (m: region) =
+  begin
+    let acs r s = if s = [] then '-' else r in
+    Format.fprintf fmt "@[<hov 2>%a: %c%c%c"
+      pp_node m.node
+      (acs 'R' m.reads) (acs 'W' m.writes) (acs 'A' m.shifts) ;
+    List.iter (Format.fprintf fmt "@ %s:") m.labels ;
+    List.iter (Format.fprintf fmt "@ %a" Varinfo.pretty) m.roots ;
+    List.iter (Format.fprintf fmt "@ (%a)" Typ.pretty) m.types ;
+    Format.fprintf fmt "@ %db" m.sizeof ;
+    Option.iter (Format.fprintf fmt "@ (*%a)" pp_node) m.pointed ;
+    Format.fprintf fmt "@[<hv 0>" ;
+    List.iter (Format.fprintf fmt "@ %a" pp_range) m.ranges ;
+    Format.fprintf fmt "@]" ;
+    if Options.debug_atleast 1 then
+      begin
+        List.iter (Format.fprintf fmt "@ R:%a" Access.pretty) m.reads ;
+        List.iter (Format.fprintf fmt "@ W:%a" Access.pretty) m.writes ;
+        List.iter (Format.fprintf fmt "@ A:%a" Access.pretty) m.shifts ;
+      end ;
+    Format.fprintf fmt " ;@]" ;
+  end
+
+let make_range (m: map) (rg: rg) : range = {
+  offset = rg.offset ;
+  length = rg.length ;
+  cells = rg.length / sizeof (get m rg.data).clayout ;
+  data = node m rg.data ;
+}
+
+let make_region (m: map) (n: node) (r: chunk) : region = {
+  node = n ;
+  parents = nodes m r.cparents ;
+  roots = Vset.elements r.croots ;
+  labels = Lset.elements r.clabels ;
+  reads = Access.Set.elements r.creads ;
+  writes = Access.Set.elements r.cwrites ;
+  shifts = Access.Set.elements r.cshifts ;
+  types = types r ;
+  sizeof = sizeof r.clayout ;
+  ranges = List.map (make_range m) @@ ranges r.clayout ;
+  pointed = Option.map (node m) (pointed r.clayout) ;
+}
+
+let region map n = make_region map n (get map n)
+
+let rec walk h m f n =
+  let n = Ufind.find m.store n in
+  let id = Store.id n in
+  try Hashtbl.find h id with Not_found ->
+    Hashtbl.add h id () ;
+    let r = Ufind.get m.store n in
+    f (make_region m n r) ;
+    match r.clayout with
+    | Blob -> ()
+    | Cell(_,p) -> Option.iter (walk h m f) p
+    | Compound(_,rg) -> Ranges.iter (walk h m f) rg
+
+let iter (m:map) (f: region -> unit) =
+  let h = Hashtbl.create 0 in
+  Vmap.iter (fun _x n -> walk h m f n) m.roots
+
+let regions map =
+  let pool = ref [] in
+  iter map (fun r -> pool := r :: !pool) ;
+  List.rev !pool
+
+(* -------------------------------------------------------------------------- *)
+(* --- Merge                                                              --- *)
+(* -------------------------------------------------------------------------- *)
+
+type queue = (node * node) Queue.t
+
+let singleton ~size = function
+  | None -> Ranges.empty
+  | Some r -> Ranges.range ~length:size r
+
+let merge_node (m: map) (q: queue) (a: node) (b: node) : node =
+  if not @@ Ufind.eq m.store a b then Queue.push (a,b) q ;
+  Ufind.find m.store (min a b)
+
+let merge_opt (m: map) (q: queue)
+    (pa : node option) (pb : node option) : node option =
+  match pa, pb with
+  | None, p | p, None -> p
+  | Some pa, Some pb -> Some (merge_node m q pa pb)
+
+let merge_range (m: map) (q: queue) (ra : rg) (rb : rg) : node =
+  let na = ra.data in
+  let nb = rb.data in
+  let ma = ra.offset + ra.length in
+  let mb = rb.offset + rb.length in
+  let dp = ra.offset - rb.offset in
+  let dq = ma - mb in
+  let sa = sizeof (get m na).clayout in
+  let sb = sizeof (get m nb).clayout in
+  let size = Ranges.(sa %. sb %. dp %. dq) in
+  let data = merge_node m q na nb in
+  if size = sa && size = sb then data else
+    merge_node m q (add_cell m ~size ()) data
+
+let merge_ranges (m: map) (q: queue)
+    (sa : int) (wa : node Ranges.t)
+    (sb : int) (wb : node Ranges.t)
+  : layout =
+  if sa = sb then
+    Compound(sa, Ranges.merge (merge_range m q) wa wb)
+  else
+    let size = Ranges.gcd sa sb in
+    let ra = Ranges.squash (merge_node m q) wa in
+    let rb = Ranges.squash (merge_node m q) wb in
+    Compound(size, singleton ~size @@ merge_opt m q ra rb)
+
+let merge_layout (m: map) (q: queue) (a : layout) (b : layout) : layout =
+  match a, b with
+  | Blob, c | c, Blob -> c
+
+  | Cell(sa,pa) , Cell(sb,pb) -> Cell(Ranges.gcd sa sb, merge_opt m q pa pb)
+
+  | Compound(sa,wa), Compound(sb,wb) -> merge_ranges m q sa wa sb wb
+
+  | Compound(sr,wr), Cell(sx,None) | Cell(sx,None), Compound(sr,wr) ->
+    let size = Ranges.gcd sx sr in
+    Compound(size, singleton ~size @@ Ranges.squash (merge_node m q) wr)
+
+  | Compound(sr,wr), Cell(sx,Some ptr) | Cell(sx,Some ptr), Compound(sr,wr) ->
+    let rp = add_cell m ~size:sx ~ptr () in
+    let wx = Ranges.range ~length:sx rp in
+    merge_ranges m q sx wx sr wr
+
+let merge_region (m: map) (q: queue) (a : chunk) (b : chunk) : chunk = {
+  cparents = nodes m @@ Store.bag a.cparents b.cparents ;
+  clabels = Lset.union a.clabels b.clabels ;
+  croots = Vset.union a.croots b.croots ;
+  creads = Access.Set.union a.creads b.creads ;
+  cwrites = Access.Set.union a.cwrites b.cwrites ;
+  cshifts = Access.Set.union a.cshifts b.cshifts ;
+  clayout = merge_layout m q a.clayout b.clayout ;
+}
+
+let do_merge (m: map) (q: queue) (a: node) (b: node): unit =
+  begin
+    let ra = Ufind.get m.store a in
+    let rb = Ufind.get m.store b in
+    let rx = Ufind.union m.store a b in
+    let rc = merge_region m q ra rb in
+    Ufind.set m.store rx rc ;
+  end
+
+let merge_all (m:map) = function
+  | [] -> ()
+  | r::rs ->
+    let q = Queue.create () in
+    List.iter (fun r' -> ignore @@ merge_node m q r r') rs ;
+    while not @@ Queue.is_empty q do
+      let a,b = Queue.pop q in
+      do_merge m q a b ;
+    done
+
+let merge (m: map) (a: node) (b: node) : node =
+  failwith_locked m "Region.Memory.merge" ;
+  merge_all m [a;b] ; Ufind.find m.store (min a b)
+
+(* -------------------------------------------------------------------------- *)
+(* --- Offset                                                             --- *)
+(* -------------------------------------------------------------------------- *)
+
+let add_field (m:map) (r:node) (fd:fieldinfo) : node =
+  let ci = fd.fcomp in
+  if ci.cstruct then
+    let size = Cil.bitsSizeOf (TComp(ci,[])) in
+    let offset, length = Cil.fieldBitsOffset fd in
+    let data = add_cell m () in
+    let rc = add_range m ~size ~offset ~length ~data in
+    ignore @@ merge m r rc ; data
+  else r
+
+let add_index (m:map) (r:node) (ty:typ) : node =
+  let size = Cil.bitsSizeOf ty in
+  let data = add_cell m () in
+  let rc = add_range m ~size ~offset:0 ~length:size ~data in
+  ignore @@ merge m r rc ; data
+
+let add_points_to (m: map) (a: node) (b : node) =
+  failwith_locked m "Region.Memory.points_to" ;
+  ignore @@ merge m a @@ add_cell m ~ptr:b ()
+
+let add_value (m:map) (rv:node) (ty:typ) : node option =
+  if Cil.isPointerType ty then
+    let rp = add_cell m () in
+    add_points_to m rv rp ;
+    Some rp
+  else
+    None
+
+(* -------------------------------------------------------------------------- *)
+(* --- Access                                                             --- *)
+(* -------------------------------------------------------------------------- *)
+
+let access (m:map) (a:node) (ty: typ) =
+  let sr = sizeof (get m a).clayout in
+  let size = Ranges.gcd sr (Cil.bitsSizeOf ty) in
+  if sr <> size then ignore (merge m a (add_cell m ~size ()))
+
+let read (m: map) (a: node) from =
+  failwith_locked m "Region.Memory.read" ;
+  let r = get m a in
+  Ufind.set m.store a { r with creads = Access.Set.add from r.creads } ;
+  access m a (Access.typeof from)
+
+let write (m: map) (a: node) from =
+  failwith_locked m "Region.Memory.write" ;
+  let r = get m a in
+  Ufind.set m.store a { r with cwrites = Access.Set.add from r.cwrites } ;
+  access m a (Access.typeof from)
+
+let shift (m: map) (a: node) from =
+  failwith_locked m "Region.Memory.shift" ;
+  let r = get m a in
+  Ufind.set m.store a { r with cshifts = Access.Set.add from r.cshifts }
+(* no access *)
+
+(* -------------------------------------------------------------------------- *)
+(* --- Lookup                                                            ---- *)
+(* -------------------------------------------------------------------------- *)
+
+let cranges m r =
+  let rg = Ufind.get m.store r in
+  match rg.clayout with
+  | Blob | Cell _ -> raise Not_found
+  | Compound(s,rgs) -> s, rgs
+
+let cpointed m r =
+  let rg = Ufind.get m.store r in
+  match rg.clayout with
+  | Blob | Compound _ | Cell(_,None) -> None
+  | Cell(_,Some r) -> Some (Ufind.find m.store r)
+
+let rec lval (m: map) (lv: lval) : node =
+  let h = host m (fst lv) in
+  offset m h (snd lv)
+
+and host (m: map) (h: lhost) : node =
+  match h with
+  | Var x -> Ufind.find m.store @@ Vmap.find x m.roots
+  | Mem e ->
+    match exp m e with
+    | None -> raise Not_found
+    | Some r -> r
+
+and offset (m: map) (r: node) (ofs: offset) : node =
+  match ofs with
+  | NoOffset -> Ufind.find m.store r
+  | Field (fd, ofs) ->
+    if fd.fcomp.cstruct then
+      let _, rgs = cranges m r in
+      let (p,w) = Cil.fieldBitsOffset fd in
+      let rg = Ranges.find p rgs in
+      if rg.offset <= p && p+w <= rg.offset + rg.length then
+        offset m rg.data ofs
+      else raise Not_found
+    else r
+  | Index (_, ofs) ->
+    let s, rgs = cranges m r in
+    match rgs with
+    | R [rg] when rg.offset = 0 && rg.length = s ->
+      offset m rg.data ofs
+    | _ -> raise Not_found
+
+and exp (m: map) (e: exp) : node option =
+  match e.enode with
+  | Const _
+  | SizeOf _ | SizeOfE _ | SizeOfStr _ | AlignOf _ | AlignOfE _ -> None
+  | Lval lv -> cpointed m @@ lval m lv
+  | AddrOf lv | StartOf lv -> Some (lval m lv)
+  | CastE(_, e) -> exp m e
+  | BinOp((PlusPI|MinusPI),p,_,_) -> exp m p
+  | UnOp (_, _, _) | BinOp (_, _, _, _) -> None
+
+(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/region/memory.mli b/src/plugins/region/memory.mli
new file mode 100644
index 0000000000000000000000000000000000000000..79a0543ef0d220ca2a4da29b4be20a8f570f899d
--- /dev/null
+++ b/src/plugins/region/memory.mli
@@ -0,0 +1,95 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_types
+
+type node
+
+type range = {
+  offset: int ;
+  length: int ;
+  cells: int ;
+  data: node ;
+}
+
+type region = {
+  node: node ;
+  parents: node list ;
+  roots: varinfo list ;
+  labels: string list ;
+  types: typ list ;
+  reads: Access.acs list ;
+  writes: Access.acs list ;
+  shifts: Access.acs list ;
+  sizeof: int ;
+  ranges: range list ;
+  pointed: node option ;
+}
+
+type map
+
+val pp_node : Format.formatter -> node -> unit
+val pp_range : Format.formatter -> range -> unit
+val pp_region : Format.formatter -> region -> unit
+
+(** Initially unlocked. *)
+val create : unit -> map
+
+(** Default locked status is inherited from the copied map. *)
+val copy : ?locked:bool -> map -> map
+
+(** Lock the map. No more access nor merge can be added into the map. *)
+val lock : map -> unit
+
+(** Unlock the map. *)
+val unlock : map -> unit
+
+val id : node -> int
+val forge : int -> node
+val node : map -> node -> node
+val nodes : map -> node list -> node list
+
+val iter : map -> (region -> unit) -> unit
+val region : map -> node -> region
+val regions : map -> region list
+
+val add_root : map -> Cil_types.varinfo -> node
+val add_label : map -> string -> node
+val add_cell : map -> ?size:int -> ?ptr:node -> ?root:varinfo -> ?label:string -> unit -> node
+val add_range : map -> size:int -> offset:int -> length:int -> data:node -> node
+val add_field : map -> node -> fieldinfo -> node
+val add_index : map -> node -> typ -> node
+val add_points_to : map -> node -> node -> unit
+val add_value : map -> node -> typ -> node option
+
+val read : map -> node -> Access.acs -> unit
+val write : map -> node -> Access.acs -> unit
+val shift : map -> node -> Access.acs -> unit
+
+val merge : map -> node -> node -> node
+val merge_all : map -> node list -> unit
+
+(** @raise Not_found *)
+val lval : map -> lval -> node
+
+(** @raise Not_found *)
+val exp : map -> exp -> node option
diff --git a/src/plugins/region/options.ml b/src/plugins/region/options.ml
new file mode 100644
index 0000000000000000000000000000000000000000..5bc8f5b2d7fef1402b6783e4975098876ecd6836
--- /dev/null
+++ b/src/plugins/region/options.ml
@@ -0,0 +1,38 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(* -------------------------------------------------------------------------- *)
+(* --- Plugin Registration                                                --- *)
+(* -------------------------------------------------------------------------- *)
+
+include Plugin.Register
+    (struct
+      let name = "Region Analysis"
+      let help = "Memory Region Analysis (experimental)"
+      let shortname = "region"
+    end)
+
+module Enabled = False
+    (struct
+      let option_name = "-region"
+      let help = "Enable Region Analysis"
+    end)
diff --git a/src/plugins/region/options.mli b/src/plugins/region/options.mli
new file mode 100644
index 0000000000000000000000000000000000000000..8aba69998a130c61bbe960c3dda7fc546eb25e66
--- /dev/null
+++ b/src/plugins/region/options.mli
@@ -0,0 +1,28 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(** Region Command Line Interface *)
+
+open Parameter_sig
+include Log.Messages
+
+module Enabled : Bool
diff --git a/src/plugins/region/ranges.ml b/src/plugins/region/ranges.ml
new file mode 100644
index 0000000000000000000000000000000000000000..54e7cc2c43fc0d8864f4332f1de80d8006c647f3
--- /dev/null
+++ b/src/plugins/region/ranges.ml
@@ -0,0 +1,94 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+let rec gcd a b =
+  if a = 0 then abs b else
+  if b = 0 then abs a else
+    gcd b (a mod b)
+
+let (%.) = gcd
+
+(* -------------------------------------------------------------------------- *)
+(* --- Range Maps                                                         --- *)
+(* -------------------------------------------------------------------------- *)
+
+type 'a range = {
+  offset: int ;
+  length: int ;
+  data: 'a ;
+}
+
+let pp_range fmt r =
+  Format.fprintf fmt "%04d..%04d" r.offset (r.offset + r.length - 1)
+
+let pp_offset fmt r =
+  Format.fprintf fmt "%04d:%04d" r.offset r.length
+
+type 'a t = R of 'a range list (* sorted, no-overlap *)
+
+let empty = R []
+
+let singleton r =
+  if not (0 <= r.offset && 0 < r.length) then
+    raise (Invalid_argument "Region.Ranges.singleton") ;
+  R [r]
+
+let range ?(offset=0) ?(length=1) data = singleton { offset ; length ; data }
+
+let rec find (k: int) = function
+  | [] -> raise Not_found
+  | ({ offset ; length } as r) :: rs ->
+    if offset <= k && k <= offset + length then r else find k rs
+
+let find k (R rs) = find k rs
+
+let rec merge f ra rb =
+  match ra, rb with
+  | [], rs | rs, [] -> rs
+  | a :: wa, b :: wb ->
+    let a' = a.offset + a.length in
+    let b' = b.offset + b.length in
+    if a' <= b.offset then
+      a :: merge f wa rb
+    else
+    if b' < a.offset then
+      b :: merge f ra wb
+    else
+      let offset = min a.offset b.offset in
+      let length = max a' b' - offset in
+      let data = f a b in
+      let r = { offset ; length ; data } in
+      if a' < b'
+      then merge f wa (r::wb)
+      else merge f (r::wa) wb
+
+let merge f (R x) (R y) = R (merge f x y)
+
+let squash f = function
+  | R [] -> None
+  | R (x::xs) -> Some (List.fold_left (fun w r -> f w r.data) x.data xs)
+
+let iteri f (R xs) = List.iter f xs
+let iter f (R xs) = List.iter (fun r -> f r.data) xs
+let map f (R xs) = R (List.map (fun r -> { r with data = f r.data }) xs)
+
+(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/region/ranges.mli b/src/plugins/region/ranges.mli
new file mode 100644
index 0000000000000000000000000000000000000000..b04c273feb06930e56c6d996efe8688f8519dd86
--- /dev/null
+++ b/src/plugins/region/ranges.mli
@@ -0,0 +1,44 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+val gcd : int -> int -> int
+val (%.) : int -> int -> int (** gcd *)
+
+type 'a range = { offset : int; length : int; data : 'a; }
+type 'a t = private R of 'a range list (* sorted, no overlap *)
+
+(** Prints [offset..last] formatted with [%04d] *)
+val pp_range : Format.formatter -> 'a range -> unit
+
+(** Prints [offset:length] formatted with [%04d] *)
+val pp_offset : Format.formatter -> 'a range -> unit
+
+val empty : 'a t
+val singleton : 'a range -> 'a t
+val range : ?offset:int -> ?length:int -> 'a -> 'a t
+val merge : ('a range -> 'a range -> 'a) -> 'a t -> 'a t -> 'a t
+val squash : ('a -> 'a -> 'a) -> 'a t -> 'a option
+
+val find : int -> 'a t -> 'a range
+val map : ('a -> 'b) -> 'a t -> 'b t
+val iter : ('a -> unit) -> 'a t -> unit
+val iteri : ('a range -> unit) -> 'a t -> unit
diff --git a/src/plugins/region/register.ml b/src/plugins/region/register.ml
new file mode 100644
index 0000000000000000000000000000000000000000..54cd5cdd62b1d0f0a2e294a2b7ebce49a6390fca
--- /dev/null
+++ b/src/plugins/region/register.ml
@@ -0,0 +1,37 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+module R = Options
+
+(* -------------------------------------------------------------------------- *)
+(* --- Region Analysis Main Entry Point                                   --- *)
+(* -------------------------------------------------------------------------- *)
+
+let main () =
+  if R.Enabled.get () then
+    begin
+      Ast.compute () ;
+      R.feedback "Analyzing regions" ;
+      Globals.Functions.iter Analysis.compute ;
+    end
+
+let () = Boot.Main.extend main
diff --git a/src/plugins/region/services.ml b/src/plugins/region/services.ml
new file mode 100644
index 0000000000000000000000000000000000000000..0b5ab56eeec696b105255e42145de87b0b25027d
--- /dev/null
+++ b/src/plugins/region/services.ml
@@ -0,0 +1,262 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_datatype
+open Server
+module Md = Markdown
+
+let package = Package.package ~plugin:"region" ~title:"Region Analysis" ()
+
+(* -------------------------------------------------------------------------- *)
+(* --- Server Data                                                        --- *)
+(* -------------------------------------------------------------------------- *)
+
+module Node : Data.S with type t = Memory.node =
+struct
+  type t = Memory.node
+  let jtype = Data.declare ~package ~name:"node" (Jindex "node")
+  let to_json n = Json.of_int @@ Memory.id n
+  let of_json js = Memory.forge @@ Json.int js
+end
+
+module NodeOpt = Data.Joption(Node)
+module NodeList = Data.Jlist(Node)
+
+module Range : Data.S with type t = Memory.range =
+struct
+  type t = Memory.range
+  let jtype = Data.declare ~package ~name:"range" @@
+    Jrecord [
+      "offset", Jnumber ;
+      "length", Jnumber ;
+      "cells", Jnumber ;
+      "data", Node.jtype ;
+    ]
+  let to_json (rg : Memory.range) =
+    Json.of_fields [
+      "offset", Json.of_int rg.offset ;
+      "length", Json.of_int rg.length ;
+      "cells", Json.of_int rg.cells ;
+      "data", Node.to_json rg.data ;
+    ]
+  let of_json _ = failwith "Region.Range.of_json"
+end
+
+module Ranges = Data.Jlist(Range)
+
+module Region: Data.S with type t = Memory.region =
+struct
+  type t = Memory.region
+
+  let roots_to_json vs =
+    let open Cil_types in
+    Json.of_list @@ List.map (fun v -> Json.of_string v.vname) vs
+
+  let labels_to_json ls =
+    Json.of_list @@ List.map Json.of_string ls
+
+  let ikind_to_char (ikind : Cil_types.ikind) =
+    match ikind with
+    | IBool | IUChar -> 'b'
+    | IChar | ISChar -> 'c'
+    | IInt -> 'i'
+    | IUInt -> 'u'
+    | IShort -> 's'
+    | IUShort -> 'r'
+    | ILong | ILongLong -> 'l'
+    | IULong | IULongLong -> 'w'
+
+  let fkind_to_char (fkind : Cil_types.fkind) =
+    match fkind with
+    | FFloat -> 'f'
+    | FDouble | FLongDouble -> 'd'
+
+  let typ_to_char (ty: Cil_types.typ) =
+    match ty with
+    | TVoid _ -> 'b'
+    | TPtr _ -> 'p'
+    | TInt(ik,_) -> ikind_to_char ik
+    | TFloat(fk,_) -> fkind_to_char fk
+    | TComp({ cstruct }, _) -> if cstruct then 'S' else 'U'
+    | TArray _ -> 'A'
+    | TNamed _ -> 'T'
+    | TEnum _ -> 'E'
+    | TFun _ -> 'F'
+    | TBuiltin_va_list _ -> 'x'
+
+  let typs_to_char (typs : Cil_types.typ list) =
+    match typs with
+    | [] -> '-'
+    | [ty] -> typ_to_char ty
+    | _ -> 'x'
+
+  let label (m: Memory.region) =
+    let buffer = Buffer.create 4 in
+    if m.reads <> [] then Buffer.add_char buffer 'R' ;
+    if m.writes <> [] then Buffer.add_char buffer 'W' ;
+    if m.pointed <> None then Buffer.add_char buffer '*'
+    else if m.reads <> [] || m.writes <> [] then
+      begin
+        Buffer.add_char buffer '(' ;
+        Buffer.add_char buffer @@ typs_to_char m.types ;
+        Buffer.add_char buffer ')' ;
+      end ;
+    Buffer.contents buffer
+
+  let pp_typ_layout s0 fmt ty =
+    let s = Cil.bitsSizeOf ty in
+    if s <> s0 then
+      Format.fprintf fmt "(%a)%%%db" Typ.pretty ty s
+    else
+      Typ.pretty fmt ty
+
+  let title (m: Memory.region) =
+    Format.asprintf "%t (%db)"
+      begin fun fmt ->
+        match m.types with
+        | [] -> Format.pp_print_string fmt "Compound"
+        | [ty] -> pp_typ_layout m.sizeof fmt ty ;
+        | ty::ts ->
+          pp_typ_layout 0 fmt ty ;
+          List.iter (Format.fprintf fmt ", %a" (pp_typ_layout 0)) ts ;
+      end m.sizeof
+
+  let jtype = Data.declare ~package ~name:"region" @@
+    Jrecord [
+      "node", Node.jtype ;
+      "roots", Jarray Jalpha ;
+      "labels", Jarray Jalpha ;
+      "parents", NodeList.jtype ;
+      "sizeof", Jnumber ;
+      "ranges", Ranges.jtype ;
+      "pointed", NodeOpt.jtype ;
+      "reads", Jboolean ;
+      "writes", Jboolean ;
+      "bytes", Jboolean ;
+      "label", Jstring ;
+      "title", Jstring ;
+    ]
+
+  let to_json (m: Memory.region) =
+    Json.of_fields [
+      "node", Node.to_json m.node ;
+      "roots", roots_to_json m.roots ;
+      "labels", labels_to_json m.labels ;
+      "parents", NodeList.to_json m.parents ;
+      "sizeof", Json.of_int @@ m.sizeof ;
+      "ranges", Ranges.to_json @@ m.ranges ;
+      "pointed", NodeOpt.to_json @@ m.pointed ;
+      "reads", Json.of_bool (m.reads <> []) ;
+      "writes", Json.of_bool (m.writes <> []) ;
+      "bytes", Json.of_bool (List.length m.types > 1) ;
+      "label", Json.of_string @@ label m ;
+      "title", Json.of_string @@ title m ;
+    ]
+
+  let of_json _ = failwith "Region.Layout.of_json"
+end
+
+module Regions = Data.Jlist(Region)
+
+(* -------------------------------------------------------------------------- *)
+(* --- Server API                                                         --- *)
+(* -------------------------------------------------------------------------- *)
+
+let map_of_localizable ~local (loc : Printer_tag.localizable) =
+  let open Printer_tag in
+  match kf_of_localizable loc with
+  | None -> raise Not_found
+  | Some kf ->
+    let domain = Analysis.find kf in
+    if local then
+      match ki_of_localizable loc with
+      | Kglobal -> domain.map
+      | Kstmt s -> Stmt.Map.find s domain.body
+    else domain.map
+
+let region_of_localizable (m: Memory.map) (loc: Printer_tag.localizable) =
+  try
+    match loc with
+    | PExp(_,_,e) -> Memory.exp m e
+    | PLval(_,_,lv) -> Some (Memory.lval m lv)
+    | PVDecl(_,_,x) -> Some (Memory.lval m (Var x,NoOffset))
+    | PStmt _ | PStmtStart _
+    | PTermLval _ | PGlobal _ | PIP _ | PType _ -> None
+  with Not_found -> None
+
+let map_of_declaration (decl : Printer_tag.declaration) =
+  match decl with
+  | SFunction kf -> (Analysis.find kf).map
+  | _ -> raise Not_found
+
+let signal = Request.signal ~package ~name:"updated"
+    ~descr:(Md.plain "Region Analysis Updated")
+
+let () = Analysis.add_hook (fun () -> Request.emit signal)
+
+let () =
+  Request.register
+    ~package ~kind:`EXEC ~name:"compute"
+    ~descr:(Md.plain "Compute regions for the given declaration")
+    ~input:(module Kernel_ast.Decl)
+    ~output:(module Data.Junit)
+    (function SFunction kf -> Analysis.compute kf | _ -> ())
+
+let () =
+  Request.register
+    ~package ~kind:`GET ~name:"regions"
+    ~descr:(Md.plain "Returns computed regions for the given declaration")
+    ~input:(module Kernel_ast.Decl)
+    ~output:(module Regions)
+    ~signals:[signal]
+    begin fun decl ->
+      try Memory.regions @@ map_of_declaration decl
+      with Not_found -> []
+    end
+
+let () =
+  Request.register
+    ~package ~kind:`GET ~name:"regionsAt"
+    ~descr:(Md.plain "Compute regions at the given marker")
+    ~input:(module Data.Jpair(Kernel_ast.Marker)(Data.Jbool))
+    ~output:(module Regions)
+    ~signals:[signal]
+    begin fun (loc,local) ->
+      try Memory.regions @@ map_of_localizable ~local loc
+      with Not_found -> []
+    end
+
+let () =
+  Request.register
+    ~package ~kind:`GET ~name:"localize"
+    ~descr:(Md.plain "Localize in the local (true) or global map (false)")
+    ~input:(module Data.Jpair(Kernel_ast.Marker)(Data.Jbool))
+    ~output:(module NodeOpt)
+    ~signals:[signal]
+    begin fun (loc,local) ->
+      try
+        let map = map_of_localizable ~local loc in
+        region_of_localizable map loc
+      with Not_found -> None
+    end
+
+(* -------------------------------------------------------------------------- *)
diff --git a/src/plugins/region/services.mli b/src/plugins/region/services.mli
new file mode 100644
index 0000000000000000000000000000000000000000..2507b13f97d60fac6b22584dd12c0d4bac2f15e5
--- /dev/null
+++ b/src/plugins/region/services.mli
@@ -0,0 +1,30 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Server
+open Request
+
+val package : Package.package
+
+module Node : Data.S with type t = Memory.node
+module Range : Output with type t = Memory.range
+module Region : Output with type t = Memory.region
diff --git a/src/plugins/region/store.ml b/src/plugins/region/store.ml
new file mode 100644
index 0000000000000000000000000000000000000000..4e964e42b373265fedea995c5665efd92e916e39
--- /dev/null
+++ b/src/plugins/region/store.ml
@@ -0,0 +1,51 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+(* -------------------------------------------------------------------------- *)
+(* --- UnionFind Store with explicit integer keys                         --- *)
+(* -------------------------------------------------------------------------- *)
+
+module Imap = Map.Make(Int)
+
+type 'a rref = int
+type 'a store = 'a Imap.t ref
+
+let new_store () = ref Imap.empty
+let copy r = ref !r
+let rid = ref 0
+
+let make s v =
+  let k = incr rid ; !rid in
+  s := Imap.add k v !s ; k
+
+let get s k = Imap.find k !s
+let set s k v = s := Imap.add k v !s
+
+let eq _s i j = (i == j)
+
+let id x = x
+let forge x = x
+let list = List.sort_uniq Int.compare
+let rec bag a b =
+  match a, b with
+  | [], c | c, [] -> c
+  | x::xs, y::ys -> x :: y :: bag xs ys
diff --git a/src/plugins/region/store.mli b/src/plugins/region/store.mli
new file mode 100644
index 0000000000000000000000000000000000000000..3d9b60a6e02e310ddfd3827ec2d2f872f304f93a
--- /dev/null
+++ b/src/plugins/region/store.mli
@@ -0,0 +1,35 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+include UnionFind.STORE
+
+(** Global unique identifier *)
+val id : 'a rref -> int
+
+(** Unordered union *)
+val bag: 'a list -> 'a list -> 'a list
+
+(** Sorted, unique *)
+val list : 'a rref list -> 'a rref list
+
+(**/**)
+val forge : int -> 'a rref
diff --git a/src/plugins/region/tests/ptests_config b/src/plugins/region/tests/ptests_config
new file mode 100644
index 0000000000000000000000000000000000000000..316166170520da8ec5a4067d38647cf887490c58
--- /dev/null
+++ b/src/plugins/region/tests/ptests_config
@@ -0,0 +1 @@
+DEFAULT_SUITES= region
diff --git a/src/plugins/region/tests/region/array1.i b/src/plugins/region/tests/region/array1.i
new file mode 100644
index 0000000000000000000000000000000000000000..eac00551a13634c34e79f284c7a0bebb362b0591
--- /dev/null
+++ b/src/plugins/region/tests/region/array1.i
@@ -0,0 +1,8 @@
+//@ region *p, *q ;
+int job( int n, int * p , int * q )
+{
+  int s = 0 ;
+  for (int k = 0; k < n; k++)
+    s += p[k] * q[k] ;
+  return s ;
+}
diff --git a/src/plugins/region/tests/region/array2.i b/src/plugins/region/tests/region/array2.i
new file mode 100644
index 0000000000000000000000000000000000000000..a9cbb53d7fffb400ff1c5b11512d50550682a934
--- /dev/null
+++ b/src/plugins/region/tests/region/array2.i
@@ -0,0 +1,8 @@
+//@ region P: *p, Q: *q ;
+int job( int n, int * p , int * q )
+{
+  int s = 0 ;
+  for (int k = 0; k < n; k++)
+    s += p[k] * q[k] ;
+  return s ;
+}
diff --git a/src/plugins/region/tests/region/fb_ADD.i b/src/plugins/region/tests/region/fb_ADD.i
new file mode 100644
index 0000000000000000000000000000000000000000..8ceb168c7af4dbce81e6776ee4fffd4b2c838ae7
--- /dev/null
+++ b/src/plugins/region/tests/region/fb_ADD.i
@@ -0,0 +1,22 @@
+typedef struct N { double v ; int s ; } *SN ;
+typedef struct L { int v ; int s ; } *SL ;
+
+typedef struct Block {
+  SN prm ;
+  SN inp1 ;
+  SN inp2 ;
+  SN inp3 ;
+  SN out1 ;
+  SN out2 ;
+  SN out3 ;
+  SL idx1 ;
+  SL idx2 ;
+  SL idx3 ;
+  SN sum ;
+} FB ;
+
+void job(FB *fb)
+{
+  fb->out1->v = fb->out1->v + fb->out2->v ;
+  fb->out1->s = fb->out1->s | fb->out2->s ;
+}
diff --git a/src/plugins/region/tests/region/fb_SORT.i b/src/plugins/region/tests/region/fb_SORT.i
new file mode 100644
index 0000000000000000000000000000000000000000..4ef65d2cfb6145f4c3266320fbeed7b8c7eb4145
--- /dev/null
+++ b/src/plugins/region/tests/region/fb_SORT.i
@@ -0,0 +1,38 @@
+typedef struct N { double v ; int s ; } *SN ;
+typedef struct L { int v ; int s ; } *SL ;
+
+typedef struct Block {
+  SN prm ;
+  SN inp1 ;
+  SN inp2 ;
+  SN inp3 ;
+  SN out1 ;
+  SN out2 ;
+  SN out3 ;
+  SL idx1 ;
+  SL idx2 ;
+  SL idx3 ;
+  SN sum ;
+} FB ;
+
+void job(FB *fb)
+{
+  SN *inp = &(fb->inp1) ;
+  SN *out = &(fb->out1) ;
+  SL *idx = &(fb->idx1) ;
+
+  for (int i = 0; i < 3; i++) {
+    out[i]->v = inp[i]->v + fb->prm->v ;
+    out[i]->s = 0 ;
+    idx[i]->v = inp[i]->s ;
+    idx[i]->s = 0 ;
+  }
+
+  fb->sum->v =
+    fb->out1->v +
+    fb->out2->v +
+    fb->out3->v ;
+
+  fb->sum->s = 0 ;
+
+}
diff --git a/src/plugins/region/tests/region/garbled.i b/src/plugins/region/tests/region/garbled.i
new file mode 100644
index 0000000000000000000000000000000000000000..6b703ba4245ba130e45827ac182b69f5dd03dfdd
--- /dev/null
+++ b/src/plugins/region/tests/region/garbled.i
@@ -0,0 +1,6 @@
+
+
+float job(int *p,int *q)
+{
+  return *q + *(float*)p + *p ;
+}
diff --git a/src/plugins/region/tests/region/oracle/array1.res.oracle b/src/plugins/region/tests/region/oracle/array1.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..32aa817c98cb58cf497d73bf7b882b710c6f01aa
--- /dev/null
+++ b/src/plugins/region/tests/region/oracle/array1.res.oracle
@@ -0,0 +1,9 @@
+[kernel] Parsing array1.i (no preprocessing)
+[region] Analyzing regions
+[region] Function job:
+  R000b: R-- n (int) 32b ;
+  R0004: R-A p (int *) 64b (*R0002) ;
+  R0002: R-- (int) 32b ;
+  R0001: R-A q (int *) 64b (*R0002) ;
+  R0007: RW- s (int) 32b ;
+  R0009: RW- k (int) 32b ;
diff --git a/src/plugins/region/tests/region/oracle/array2.res.oracle b/src/plugins/region/tests/region/oracle/array2.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..03db359956cffee155c75a0a93f0336392dcd450
--- /dev/null
+++ b/src/plugins/region/tests/region/oracle/array2.res.oracle
@@ -0,0 +1,10 @@
+[kernel] Parsing array2.i (no preprocessing)
+[region] Analyzing regions
+[region] Function job:
+  R000d: R-- n (int) 32b ;
+  R0001: R-A p (int *) 64b (*R0004) ;
+  R0004: R-- P: (int) 32b ;
+  R0005: R-A q (int *) 64b (*R0008) ;
+  R0008: R-- Q: (int) 32b ;
+  R0009: RW- s (int) 32b ;
+  R000b: RW- k (int) 32b ;
diff --git a/src/plugins/region/tests/region/oracle/fb_ADD.res.oracle b/src/plugins/region/tests/region/oracle/fb_ADD.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..84e4fe84a136c3e51118270f99079d75072b4153
--- /dev/null
+++ b/src/plugins/region/tests/region/oracle/fb_ADD.res.oracle
@@ -0,0 +1,13 @@
+[kernel] Parsing fb_ADD.i (no preprocessing)
+[region] Analyzing regions
+[region] Function job:
+  R0001: R-- fb (FB *) 64b (*R0003) ;
+  R0003: --- 704b 256..320 [1]: R0005 320..384 [1]: R0018 ;
+  R0005: R-- (struct N *) 64b (*R0008) ;
+  R0008: --- 128b 0..64 [1]: R000a 64..96 [1]: R0027 ;
+  R000a: RW- (double) 64b ;
+  R0027: RW- (int) 32b ;
+  R0018: R-- (struct N *) 64b (*R001b) ;
+  R001b: --- 128b 0..64 [1]: R001d 64..96 [1]: R003a ;
+  R001d: R-- (double) 64b ;
+  R003a: R-- (int) 32b ;
diff --git a/src/plugins/region/tests/region/oracle/fb_SORT.res.oracle b/src/plugins/region/tests/region/oracle/fb_SORT.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..912201cee70ff9cb9ad26b705ce0a1674f0ab3e1
--- /dev/null
+++ b/src/plugins/region/tests/region/oracle/fb_SORT.res.oracle
@@ -0,0 +1,33 @@
+[kernel] Parsing fb_SORT.i (no preprocessing)
+[region] Analyzing regions
+[region] Function job:
+  R0003: R-- fb (FB *) 64b (*R0005) ;
+  R0005: ---
+    704b
+        0..128 [2]: R0007
+        256..320 [1]: R000e
+        320..384 [1]: R0066
+        384..512 [2]: R0015
+        640..704 [1]: R0053 ;
+  R0007: R-- (struct N *) 64b (*R0024) ;
+  R0024: --- 128b 0..64 [1]: R0026 64..96 [1]: R0046 ;
+  R0026: R-- (double) 64b ;
+  R0046: R-- (int) 32b ;
+  R000e: R-- (struct N *) 64b (*R001d) ;
+  R001d: --- 128b 0..64 [1]: R001f 64..96 [1]: R0038 ;
+  R001f: RW- (double) 64b ;
+  R0038: -W- (int) 32b ;
+  R0066: R-- (struct N *) 64b (*R0069) ;
+  R0069: --- 128b 0..64 [1]: R006b ;
+  R006b: R-- (double) 64b ;
+  R0015: R-- (struct N *) (struct L *) 64b (*R003e) ;
+  R003e: --- 64b 0..64 [2]: R0040 ;
+  R0040: RW- (int) (double) 32b ;
+  R0053: R-- (struct N *) 64b (*R0056) ;
+  R0056: --- 128b 0..64 [1]: R0058 64..96 [1]: R007f ;
+  R0058: -W- (double) 64b ;
+  R007f: -W- (int) 32b ;
+  R0001: RWA inp (SN *) 64b (*R0007) ;
+  R000a: RWA out (SN *) 64b (*R000e) ;
+  R0011: RWA idx (SL *) 64b (*R0015) ;
+  R0018: RW- i (int) 32b ;
diff --git a/src/plugins/region/tests/region/oracle/garbled.res.oracle b/src/plugins/region/tests/region/oracle/garbled.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..024e97e7a347465d0c35c9340e432a711cbe9d92
--- /dev/null
+++ b/src/plugins/region/tests/region/oracle/garbled.res.oracle
@@ -0,0 +1,8 @@
+[kernel] Parsing garbled.i (no preprocessing)
+[region] Analyzing regions
+[region] Function job:
+  R0007: R-- p (int *) 64b (*R0009) ;
+  R0009: R-- (int) (float) 32b ;
+  R0002: R-- q (int *) 64b (*R0004) ;
+  R0004: R-- (int) 32b ;
+  R0001: RW- __retres (float) 32b ;
diff --git a/src/plugins/region/tests/region/oracle/swap.res.oracle b/src/plugins/region/tests/region/oracle/swap.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..3117fb686d5ee476901cd95a19ab05fd51194ca0
--- /dev/null
+++ b/src/plugins/region/tests/region/oracle/swap.res.oracle
@@ -0,0 +1,13 @@
+[kernel] Parsing swap.i (no preprocessing)
+[region] Analyzing regions
+[region] Function swap_aliased:
+  R0004: R-- a (int *) 64b (*R0007) ;
+  R0007: RW- R: (int) 32b ;
+  R0001: R-- b (int *) 64b (*R0007) ;
+  R0008: RW- tmp (int) 32b ;
+[region] Function swap_separated:
+  R0013: R-- a (int *) 64b (*R0016) ;
+  R0016: RW- A: (int) 32b ;
+  R0017: R-- b (int *) 64b (*R001a) ;
+  R001a: RW- B: (int) 32b ;
+  R001b: RW- tmp (int) 32b ;
diff --git a/src/plugins/region/tests/region/swap.i b/src/plugins/region/tests/region/swap.i
new file mode 100644
index 0000000000000000000000000000000000000000..04816ea5fd84319d9dad1009e3cbdb643cea9272
--- /dev/null
+++ b/src/plugins/region/tests/region/swap.i
@@ -0,0 +1,29 @@
+/*@
+  requires \valid(a);
+  requires \valid(b);
+  region A: *a, B: *b;
+  ensures *a == \old(*b);
+  ensures *b == \old(*a);
+  assigns *a, *b;
+  */
+void swap_separated(int *a, int *b)
+{
+  int tmp = *a ;
+  *a = *b;
+  *b = tmp ;
+}
+
+/*@
+  requires \valid(a);
+  requires \valid(b);
+  region R: *a, *b;
+  ensures *a == \old(*b);
+  ensures *b == \old(*a);
+  assigns *a, *b;
+  */
+void swap_aliased(int *a, int *b)
+{
+  int tmp = *a ;
+  *a = *b;
+  *b = tmp ;
+}
diff --git a/src/plugins/region/tests/test_config b/src/plugins/region/tests/test_config
new file mode 100644
index 0000000000000000000000000000000000000000..4772d6b36660012dd7b95cfdc8bed12676c83fb1
--- /dev/null
+++ b/src/plugins/region/tests/test_config
@@ -0,0 +1,2 @@
+PLUGIN: region
+OPT: -region
diff --git a/src/plugins/server/data.ml b/src/plugins/server/data.ml
index 5943bf96b4fa603a22956eea666dafb2db5450a6..365ac6ec02a55a33b4f1376f5540718658d92d64 100644
--- a/src/plugins/server/data.ml
+++ b/src/plugins/server/data.ml
@@ -88,16 +88,14 @@ module Joption(A : S) : S with type t = A.t option =
 struct
   type t = A.t option
 
-  let nullable = try ignore (A.of_json `Null) ; true with _ -> false
-  let jtype = Joption (if not nullable then A.jtype else Jtuple [A.jtype])
+  let jtype = Joption A.jtype
 
   let to_json = function
     | None -> `Null
-    | Some v -> if nullable then `List [A.to_json v] else A.to_json v
+    | Some v -> A.to_json v
 
   let of_json = function
     | `Null -> None
-    | `List [js] when nullable -> Some (A.of_json js)
     | js -> Some (A.of_json js)
 
 end
diff --git a/src/plugins/wp/RegionAnnot.ml b/src/plugins/wp/RegionAnnot.ml
index dd4561560a3c2d839554448924670aa65437006b..f2697affd43569089fda263bbf4cb02213b1f987 100644
--- a/src/plugins/wp/RegionAnnot.ml
+++ b/src/plugins/wp/RegionAnnot.ml
@@ -468,7 +468,7 @@ let typecheck typing_context _loc ps =
 
 let of_extid = Hashtbl.find registry
 let of_extrev = function
-  | { ext_name="region" ; ext_kind = Ext_id k } -> of_extid k
+  | { ext_name="wpregion" ; ext_kind = Ext_id k } -> of_extid k
   | _ -> raise Not_found
 let of_extension e = List.rev (of_extrev e)
 let of_behavior bhv =
@@ -498,7 +498,7 @@ let register () =
   then
     begin
       registered := true ;
-      Acsl_extension.register_behavior ~plugin:"wp" "region"
+      Acsl_extension.register_behavior ~plugin:"wp" "wpregion"
         typecheck ~printer:pp_extension false
     end
 
diff --git a/src/plugins/wp/tests/wp_region/annot.i b/src/plugins/wp/tests/wp_region/annot.i
index e141f13431ac5907cd8a06d8b5d17392d761195d..2312dafd8a402817ff5cc572dee05384826bd01c 100644
--- a/src/plugins/wp/tests/wp_region/annot.i
+++ b/src/plugins/wp/tests/wp_region/annot.i
@@ -31,7 +31,7 @@ typedef struct Block {
   SN sum ;
 } FB ;
 
-//@ \wp::region *fb ;
+//@ \wp::wpregion *fb ;
 void fb_ADD(FB *fb)
 {
   fb->out1->v = fb->out1->v + fb->out2->v ;
@@ -39,9 +39,9 @@ void fb_ADD(FB *fb)
 }
 
 /*@
-  \wp::region IN:   (fb->inp1 .. fb->inp3), \pattern{PMEM} ;
-  \wp::region OUT:  (fb->out1 .. fb->out3), \pattern{PVECTOR} ;
-  \wp::region IDX:  (fb->idx1 .. fb->idx3), \pattern{PVECTOR} ;
+  \wp::wpregion IN:   (fb->inp1 .. fb->inp3), \pattern{PMEM} ;
+  \wp::wpregion OUT:  (fb->out1 .. fb->out3), \pattern{PVECTOR} ;
+  \wp::wpregion IDX:  (fb->idx1 .. fb->idx3), \pattern{PVECTOR} ;
  */
 void fb_SORT(FB *fb)
 {
diff --git a/src/plugins/wp/tests/wp_region/annot/a.i b/src/plugins/wp/tests/wp_region/annot/a.i
index e684198b0e99768b84e7c3cf7aa9135e77220c27..fb6d84c2cb8cf463ae9e13dd1b6a159325899843 100644
--- a/src/plugins/wp/tests/wp_region/annot/a.i
+++ b/src/plugins/wp/tests/wp_region/annot/a.i
@@ -24,7 +24,7 @@ struct Block {
 };
 typedef struct Block FB;
 /*@ terminates \true;
-    \wp::region *fb; */
+    \wp::wpregion *fb; */
 void fb_ADD(FB *fb)
 {
   (fb->out1)->v += (fb->out2)->v;
@@ -33,9 +33,9 @@ void fb_ADD(FB *fb)
 }
 
 /*@ terminates \true;
-    \wp::region IN: \pattern{PMEM}, (fb->inp1..fb->inp3);
-    \wp::region OUT: \pattern{PVECTOR}, (fb->out1..fb->out3);
-    \wp::region IDX: \pattern{PVECTOR}, (fb->idx1..fb->idx3);
+    \wp::wpregion IN: \pattern{PMEM}, (fb->inp1..fb->inp3);
+    \wp::wpregion OUT: \pattern{PVECTOR}, (fb->out1..fb->out3);
+    \wp::wpregion IDX: \pattern{PVECTOR}, (fb->idx1..fb->idx3);
  */
 void fb_SORT(FB *fb)
 {
@@ -56,5 +56,3 @@ void fb_SORT(FB *fb)
   (fb->sum)->s = 0;
   return;
 }
-
-
diff --git a/src/plugins/wp/tests/wp_region/annot/b.i b/src/plugins/wp/tests/wp_region/annot/b.i
index e684198b0e99768b84e7c3cf7aa9135e77220c27..fb6d84c2cb8cf463ae9e13dd1b6a159325899843 100644
--- a/src/plugins/wp/tests/wp_region/annot/b.i
+++ b/src/plugins/wp/tests/wp_region/annot/b.i
@@ -24,7 +24,7 @@ struct Block {
 };
 typedef struct Block FB;
 /*@ terminates \true;
-    \wp::region *fb; */
+    \wp::wpregion *fb; */
 void fb_ADD(FB *fb)
 {
   (fb->out1)->v += (fb->out2)->v;
@@ -33,9 +33,9 @@ void fb_ADD(FB *fb)
 }
 
 /*@ terminates \true;
-    \wp::region IN: \pattern{PMEM}, (fb->inp1..fb->inp3);
-    \wp::region OUT: \pattern{PVECTOR}, (fb->out1..fb->out3);
-    \wp::region IDX: \pattern{PVECTOR}, (fb->idx1..fb->idx3);
+    \wp::wpregion IN: \pattern{PMEM}, (fb->inp1..fb->inp3);
+    \wp::wpregion OUT: \pattern{PVECTOR}, (fb->out1..fb->out3);
+    \wp::wpregion IDX: \pattern{PVECTOR}, (fb->idx1..fb->idx3);
  */
 void fb_SORT(FB *fb)
 {
@@ -56,5 +56,3 @@ void fb_SORT(FB *fb)
   (fb->sum)->s = 0;
   return;
 }
-
-
diff --git a/src/plugins/wp/tests/wp_region/array1.i b/src/plugins/wp/tests/wp_region/array1.i
index e7b235b05c2dae8fb2c2fc358b0b5a3b69461907..bf3da7a503c8df46be495fd87a2d73c2c600b6c6 100644
--- a/src/plugins/wp/tests/wp_region/array1.i
+++ b/src/plugins/wp/tests/wp_region/array1.i
@@ -1,4 +1,4 @@
-//@ \wp::region *p, *q ;
+//@ \wp::wpregion *p, *q ;
 int job( int n, int * p , int * q )
 {
   int s = 0 ;
diff --git a/src/plugins/wp/tests/wp_region/array2.i b/src/plugins/wp/tests/wp_region/array2.i
index 1bc2f1baac572e05e322c75b69d676f993d21501..be59f8c030d72f6deb45f68ddcb7e2102e840a61 100644
--- a/src/plugins/wp/tests/wp_region/array2.i
+++ b/src/plugins/wp/tests/wp_region/array2.i
@@ -1,4 +1,4 @@
-//@ \wp::region *p; \wp::region *q ;
+//@ \wp::wpregion *p; \wp::wpregion *q ;
 int job( int n, int * p , int * q )
 {
   int s = 0 ;
diff --git a/src/plugins/wp/tests/wp_region/fb_ADD.i b/src/plugins/wp/tests/wp_region/fb_ADD.i
index 2c939c92e0be417532ac6033f0fa9de7dc04779f..dc378f6d5e6eab79016343050256ad68545f2b7e 100644
--- a/src/plugins/wp/tests/wp_region/fb_ADD.i
+++ b/src/plugins/wp/tests/wp_region/fb_ADD.i
@@ -16,7 +16,7 @@ typedef struct Block {
 } FB ;
 
 /*@
-  \wp::region A: fb ;
+  \wp::wpregion A: fb ;
 */
 void job(FB *fb)
 {
diff --git a/src/plugins/wp/tests/wp_region/fb_SORT.i b/src/plugins/wp/tests/wp_region/fb_SORT.i
index f5a79d97a696e5ab2ffd3fa2afcb28cd44a2fa1c..72629334e8a760da79b4436a7880cd15e41da5aa 100644
--- a/src/plugins/wp/tests/wp_region/fb_SORT.i
+++ b/src/plugins/wp/tests/wp_region/fb_SORT.i
@@ -16,10 +16,10 @@ typedef struct Block {
 } FB ;
 
 /*@
-  \wp::region Shared: *(fb->inp1 .. fb->inp3);
-  \wp::region IN:      (fb->inp1 .. fb->inp3);
-  \wp::region OUT:     (fb->out1 .. fb->out3);
-  \wp::region IDX:     (fb->idx1 .. fb->idx3);
+  \wp::wpregion Shared: *(fb->inp1 .. fb->inp3);
+  \wp::wpregion IN:      (fb->inp1 .. fb->inp3);
+  \wp::wpregion OUT:     (fb->out1 .. fb->out3);
+  \wp::wpregion IDX:     (fb->idx1 .. fb->idx3);
  */
 void job(FB *fb)
 {
diff --git a/src/plugins/wp/tests/wp_region/oracle/annot.res.oracle b/src/plugins/wp/tests/wp_region/oracle/annot.res.oracle
index 07f96795974121ec4b16386336d86296438694fc..11a2a15408554226d844aa54f49e22a8df444ae6 100644
--- a/src/plugins/wp/tests/wp_region/oracle/annot.res.oracle
+++ b/src/plugins/wp/tests/wp_region/oracle/annot.res.oracle
@@ -24,7 +24,7 @@ struct Block {
    SN sum ;
 };
 typedef struct Block FB;
-/*@ \wp::region *fb; */
+/*@ \wp::wpregion *fb; */
 void fb_ADD(FB *fb)
 {
   (fb->out1)->v += (fb->out2)->v;
@@ -32,9 +32,9 @@ void fb_ADD(FB *fb)
   return;
 }
 
-/*@ \wp::region IN: \pattern{PMEM}, (fb->inp1..fb->inp3);
-    \wp::region OUT: \pattern{PVECTOR}, (fb->out1..fb->out3);
-    \wp::region IDX: \pattern{PVECTOR}, (fb->idx1..fb->idx3);
+/*@ \wp::wpregion IN: \pattern{PMEM}, (fb->inp1..fb->inp3);
+    \wp::wpregion OUT: \pattern{PVECTOR}, (fb->out1..fb->out3);
+    \wp::wpregion IDX: \pattern{PVECTOR}, (fb->idx1..fb->idx3);
  */
 void fb_SORT(FB *fb)
 {
diff --git a/src/plugins/wp/tests/wp_region/structarray1.i b/src/plugins/wp/tests/wp_region/structarray1.i
index 474cab2dd54d19aa8f53bb0618ac98aaec6ec373..e8fb0e753eb65c3fb5fd35c27ba3bd74b16cf5e9 100644
--- a/src/plugins/wp/tests/wp_region/structarray1.i
+++ b/src/plugins/wp/tests/wp_region/structarray1.i
@@ -6,7 +6,7 @@ typedef struct Matrix {
   int coef[4][4];
 } * matrix ;
 
-//@ \wp::region *X , *R ;
+//@ \wp::wpregion *X , *R ;
 void job( matrix M , vector X , vector R )
 {
   for (int i = 0; i < 4; i++) {