Biểu diễn ngữ nghĩa và chương trình

Một phần của tài liệu Nghiên cứu kỹ thuật giải thích trừu tượng (Trang 57)

Để biểu diễn ngữ nghĩa cụ thể của các lệnh trong chương trình TVLA sử dụng các actions. Các actions có thể có tham số và sử dụng logic vị từ để mô tả quá trình chuyển từ một trạng thái tới một trạng thái khác khi lệnh được thực thi. Trong thực tế, tác động trong quá trình chuyển trạng thái được hoàn thành bằng việc sử dụng một tập các công thức vị từ cập nhật (Predicate- update formulae). Các vị từ, các công thức logic và các vị từ cập nhật, được cập nhật lại sau khi chuyển qua trạng thái mới.

Để biểu diễn ngữ nghĩa cụ thể của chương trình, TVLA sử dụng thêm đồ thị luồng điều khiển (Control Flow Graph - CFG), và các cạnh của CFG

được gắn thể hiện của các actions.

Ví dụ: Cho heap trừu tượng trong Hình 3.3 là trạng thái vào và giả sử

56

lập trình điều này có thể được biểu diễn dưới dạng x.f = null. Để biểu diễn quá trình này chúng ta đưa ra một action là setFieldNull(c, n), có thể được sử

dụng để thiết đặt một trường của một nút được trỏ tới bởi một biến trong

chương trình tới giá trị null. Sau đó chúng ta thể hiện action này với x (tương ứng nút n1) và trường f dưới dạng tham số. Cuối cùng là gắn thể hiện của action này với x và f lên cạnh của CFG. Chi tiết được cài đặt theo định dạng

TVP trong TVLA như sau: // Vị từ %p x(v_1) unique %p f(v_1,v_2) function %% // Các actions %action setFieldNull(c,n) { { n(v_1,v_2) = n(v_1,v_2) & !c(v_1) } } %% // CFG

start setNextNull(x,f) end

TVLA sẽ tự động sinh ngữ nghĩa trừu tượng từ ngữ nghĩa cụ thể của chương trình (biểu diễn qua các actions bằng logic vị từ) để thực thi. Kết quả tạo ra heap trừu tượng đầu ra được thể hiện trong Hình 3.4

Hình 3.4: Kết quả heap trừu tượng đầu ra

Tệp TVS đặc tả heap trừu tượng trên được TVLA xuất ra có nội dung như sau: %n = {n1, n2} %p = { x = {n1} f = {n2 -> n2: 1/2} sm = {n2: 1/2} }

57 3.2.5. Ví dụ về phân tích chương trình

Xét đoạn mã chương trình cần phân tích như sau:

/* list.h */

typedef struct node{ struct node *n; int data; } *L; /* reverse.c */ #include ‘‘list.h’’ L reverse(L x){ L y, t; y = NULL; while (x != NULL){ t = y; y = x; x = x->n; y->n = t; t = NULL; } return y; } (a) (b)

(a) là khai báo một kiểu dữ liệu danh sách liên kết trong ngôn ngữ C. (b) là một hàm của C đảo ngược danh sách x thành danh sách y

Trong ví dụ này, một cấu trúc 2 – valued logic đại diện cho một trạng thái bộ nhớ, với ý nghĩa của các vị từ chính và vị từ thiết lập được đưa ra trong Bảng 3.1 dưới đây:

Vị từ Ý nghĩa Định nghĩa công thức x(v) Con trỏ x trỏ đến vị trí của vùng nhớ v

n(v1,v2) n là số con trỏ để đi từ v1 đến v2

r[n,x](v) Có thể truy cập đến vùng nhớ v của con

trỏ x, thông qua n con trỏ. ∃ : ( ( ) ∧ ∗( , ))

c[n](v) Vùng nhớ v xuất hiện trên chu trình có

độ dài n ( , )

t[n](v1,v2) Biểu diễn v1 tới được v2 theo phản xạ bắc cầu

is[n](v) Vùng nhớ v có thể được trỏ đến bởi n con trỏ khác

∃ , : ( , ) ∧

58

sm(v) v là đại diện cho nhiều cấu trúc con trỏ khác (nút đại diện).

Trong đó: ∗( 1, ) = 1 nếu trong tồn tại một đường đi từ 1 đến , đường đi này có số cạnh là 0 hoặc nhiều hơn n.

Trong quá trình phân tích thì các vị từ sẽ được cập nhật đề phù hợp với trạng thái hiện thời của đối tượng. Sau khi trừu tượng hóa các trạng thái hoạt động của chương trình, ta sẽ có cấu trúc CFG của chương trình.

Giả sử ta có CFG thể biểu diễn chương trình dưới dạng trạng thái bộ nhớ lưu trữ 4 cấu trúc của chương trình khi chạy:

Hình 3.5: Trạng thái của chương trình sử dụng cấu trúc 2-valued logic Tiếp theo là việc trừu tượng hóa bằng cách sử dụng các nút đại diện để mô tả các trạng thái có thể xảy ra.

Hình 3.6: Trạng thái của chương trình sử dụng 3-valued logic Cấu trúc 3-valued 4 thể hiện trong hình 3.6 đại diện cho cấu trúc 2- valued 3 cho ( 0) = à ( ) = ( ) = ( ) = . Trong thực tế,

59

cấu trúc thể hiện trong hình 3.6 đại diện cho tất cả các danh sách với hai hoặc nhiều hơn các phần tử.

Sau khi đã có đầy đủ việc mô tả trừu tượng của chương trình, việc sử dụng phân tích của giải thích trừu tượng sẽ thực hiện. Bắt đầu từ trạng thái ban đầu S0 của chương trình, xét các trạng thái tiếp theo thông qua CFG. Lặp lại thao tác trên cho tới khi kết thúc CFG.

Hình 3.7: Biểu diễn quá trình làm việc của phân tích chương trình 3.3. Thuật toán sinh hệ ràng buộc Coerce và thuật toán giải hệ ràng buộc tìm điểm cố định Focus

3.3.1. Công thức

Các tính chất của cấu trúc có thể được trích xuất bằng cách đánh giá công thức, ta sử dụng logic first-order với bao đóng bắc cầu và đẳng thức, nhưng không có các ký hiệu hàm và ký hiệu liên tục. Ở đây, ∗ biểu diễn bao đóng phản xạ bắc cầu của các vị từ . Do đó, trong mọi cấu trúc , ( 1)

nhận giá trị 1 nếu 1 là nút được trỏ đến bởi và ∗( 1, ) nhận giá trị là 1 trong nếu có tồn tại một đường đi bằng không hay nhiều hơn cạnh từ 1

60

đến . Cột thứ ba của bảng 3.1 biểu diễn các công thức định nghĩa của tất cả các các vị từ đo được sử dụng trong ví dụ ờ phần 3.2.5 [13].

∃ 1: ( ( 1) ∧ ∗ ( 1, ))

3.3.2. Các lớp con của công thức

Định nghĩa 3.2: Một mệnh đề Horn là một công thức có dạng

( ⋀ ) → , khi > 1, và là một công thức nguyên tử,

Cho một công thức , chúng ta định nghĩa ≡ à ≡ ¬ . Một mệnh đề Horn mở rộng là một công thức có dạng

⋀ ( ) ) → ( ) , khi > 1, là một công thức nguyên tử,

∈ {0,1} [13]. 3.3.3. Ngữ nghĩa

Định nghĩa 3.3: Một phép gán là một hàm từ một biến được giải phóng tới các vị từ đơn nguyên (ví dụ, một phép gán có các hàm

: { 1, 2 . . . } → ). Phép gán được định nghĩa trên tất cả các biến được giải phóng của một công thức được gọi là đầy đủ.

Ý nghĩa của một công thức , ký hiệu là ⟦ ⟧ ( ), xác định một giá trị chân lý trong {0,1,1/2} và được định nghĩa quy nạp như sau:

Đơn nguyên: Cho ký hiệu logic ∈ {0,1,1/2}, ⟦ ⟧ ( ) = (khi

∈ {0,1,1/2}). Đối với một công thức đơn nguyên ( 1, . . . , ),

⟦ ( 1, . . . , 2)⟧ ( ) = ( ( 1), . . . , ( ))

Cho mỗi một công thức đơn nguyên ( 1 = 2),

⟦ 1 = 2⟧ ( ) =

0 ( 1) ≠ ( 2) 1 ( 1) = ( 2) à ( ( 1)) = 0 1

2 ò ạ

Từ nối logic: Cho các công thức logic và

⟦ ∧ ⟧ ( ) = (⟦ ⟧ ( ), ⟦ ⟧ ( ))

61

⟦¬ ⟧ ( ) = 1 − ⟦ ⟧ ( )

Phép lượng hóa Nếu là một công thức logic,

⟦∀ 1: ⟧ ( ) = min

∈ ⟦ ⟧ ( [ 1 ⟼ ]) ⟦∃ 1: ⟧ ( ) = max

∈ ⟦ ⟧ ( [ 1 ⟼ ])

Bao đóng bắc cầu: cho ( 1, 2: )( 3, 4),

⟦ 1, 2: )( 3, 4)⟧ ( ) = max , ,..., ∈ ( ) , ( ) min , ⟦ ⟧ ( [ 1 ⟼ , 2 ⟼ ])

Ta nói rằng và có khả năng thỏa mãn (ký hiệu là , ⊨ ) nếu

⟦ ⟧ ( ) = 1/2 ℎ ặ ⟦ ⟧ ( ) = 1. Cuối cùng, chúng ta viết ⊨ nếu với mọi : , ⊨ .

Định lý nhúng chỉ ra rằng bất kỳ công thức nào đưa ra một giá trị xác định trong một cấu trúc 3-giá trị đều được dùng để xác định giá trị giống nhau trong cấu trúc 2-valued logic được nhúng vào trong cấu trúc 3-valued logic. Định lý nhúng là nền tảng cho việc sử dụng của 3-valued logic trong phân tích tĩnh: nó đảm bảo tính hợp lý để giải thích lại về cấu trúc 3-valued logic của các công thức khi công thức đó được biểu diễn trong cấu trúc 2-valued logic để định nghĩa các ngữ nghĩa hoạt động của chương trình [13].

3.3.4. Focus

Thứ nhất, thực hiện Focus chuyển đổi cấu trúc đầu vào thành một tập đã được tinh chỉnh của các cấu trúc đại diện cho cấu trúc 2-valued logic. Với một công thức, Focus đảm bảo rằng các công thức không bao giờ mang giá trị là 1/2 trong cấu trúc Focus. Focus (và Coerce) thực hiện việc đơn giản hóa ngữ nghĩa, tức là, chúng chuyển một cấu trúc 3-valued logic vào một tập cấu trúc 3-valued logic biểu diễn cho các trạng thái trên cùng một bộ nhớ. Một thuật toán cho Focus của một công thức tổng quát được đưa ra trong phần 3.3.7

62

Hình 3.8: Ứng dụng của giải thích trừu tượng cho phân tích câu lệnh

x= x-> n cho hàm đảo ngược danh sách liên kết trong phần 3.2.5

Trong ví dụ ở phần 3.2.5, công thức Focus được hiểu là ∃ 1: ( 1) ∧ ( 1, ), trong đó định nghĩa các giá trị của biến sau khi thực hiện action

Get_Next_L(x, x) (tương ứng với câu lệnh x=x->n). Focus trên công thức

này đảm bảo rằng ( ) là xác định tại mỗi nút trong mỗi cấu trúc sau mỗi action. Hình 3.8 biểu diễn cấu trúc là Focus cho action này. Ba trường

63

hợp được xem xét trong việc tinh chỉnh : (i) Các trường của không trỏ đến bất kỳ phân tử trong danh sách đại diện bởi ( ); (ii) Trường n của u0

trỏ đến tất cả các phần tử danh sách đại diện bởi ( ); và (iii) Các trường n

của u0 trỏ tới chỉ một số các phần tử danh sách đại diện bởi ( ): u được tách thành hai nút – một nút được trỏ đến bởi trường n của u0 được đại diện

bởi u.1, và một nút không trỏ đến bởi trường n của u0 được đại diện bởi u.0. Sau Focus, điều kiện được đánh giá. Nếu công thức điều kiện là có khả năng đáp ứng, sau đó các action sẽ được thực hiện; nếu không, các action bị bỏ qua. Cơ chế này có lợi cho (một phần) giải thích điều kiện chương trình.

Trong ví dụ phần 3.2.5, vòng lặp while(x!=NULL) có hai cạnh ra trong CFG: một với các điều kiện ¬(∃ : ( )), chỉ ra rằng nếu x=NULL câu lệnh sau vòng lặp được thực thi (đầu ra trong trường hợp của chúng ta). Các cạnh khác có điều kiện: ∃ : ( ), chỉ ra rằng nếu x!=NULL thân vòng lặp được thực thi [13].

3.3.5 Công thức cập nhật

Tác dụng của ngữ nghĩa hoạt động với câu lệnh được mô tả bởi một tập các bản cập nhật công thức xác định giá trị của mỗi vị từ sau hành động của câu lệnh đó. Định lý nhúng cho phép ta đánh giá lại các các công thức trong các cấu trúc trừu tượng và cho biết kết quả cung cấp là một ngữ nghĩa trừu tượng bảo toàn. Nếu không có công thức cập nhật được quy định cho một vị từ, thì nó được giữ nguyên bởi các hành động.

Trong hình 3.8, hiệu quả của các action Get_Next_L(x=x->n) được tính bằng cách sử dụng công thức cập nhật sau đây: (i) ( ) = ∃ 1: ( 1) ∧

( 1, ), (ii) [ , ]( ) = [ , ]( ) ∧ ( [ ]( ) ∨ ¬ ( )). Công thức cập nhật (i) biến là phần tử tiếp theo với gốc là . Công thức (ii) cập nhật thông tin về những nút có thể truy cập từ sau mỗi action.

Một nút là có thể đạt từ sau hành động nếu nó là có thể đạt từ trước hành động , trừ trường hợp các nút trực tiếp được trỏ đến bởi (trừ khi

64

xuất hiện trên một chu trình n, trong trường hợp các nút được trỏ đến bởi x là

vẫn có thể truy cập mặc dù chúng ta tiến đến phần tử kế tiếp của nó). Đối

với Sf2, công thức cập nhật cho x được gán giá trị là 1 cho v→ u.1 và 0 đối với tất cả các nút khác so với u.1. Do đó, sau action các kết quả cấu trúc So2 có thể là ( . 1) = 1 nhưng ( . 0) = 0 và ( 0) = 0.

Giai đoạn cuối cùng của việc tính toán là hoạt động Coerce, trong đó sử dụng một tập các quy tắc phi mâu thuẫn để làm cho cấu trúc chính xác hơn bằng cách loại bỏ các giá trị vô hạn không cần thiết và loại bỏ các cấu trúc không khả thi. Tập hợp các quy tắc phi mâu thuẫn được sử dụng là độc lập với hành động hiện tại đang được thực hiện. Trong phần 3..3.6 cho một mô tả chi tiết về các thuật toán Coerce được sử dụng trong TVLA và cách TVLA tự động tạo ra các quy tắc phi mâu thuẫn từ các vị từ đo và các hàm thuộc tính của các vị từ.

Ví dụ, hình 3.8 cho thấy cách thức vận hành Coerce để cải thiện độ

chính xác. Cấu trúc So0 là không khả thi bởi vì các nút u phải được truy cập từ y (khi [ , ] ( ) = 1) và đây không phải là trường hợp của So0. Trong cấu

trúc So1, u không còn là một nút tóm tắt bởi vì x là duy nhất; u của vòng lặp được loại bỏ bởi vì u đã có trường n trỏ đến và nó không đại diện cho một

phần danh sách chia sẻ ( [ ] ( ) = 0).

Tương tự như vậy, trong cấu trúc So2, u.1 không còn là một nút tóm tắt; Ngoài ra, các phần tử danh sách đại diện bởi u.1 đã có một trường n trỏ đến

và nó không được chia sẻ ( [ ] ( . 1) = 0), do đó u.1 được xóa bỏ. Tương tự ta cũng có, cạnh vô hạn n từ u.0 tới u.1 được xóa bỏ.

Để đảm bảo rằng việc phân tích là dừng đối với chương trình có chứa vòng lặp thì đối với mỗi phân tích, chúng ta chọn một tập các vị từ đơn

nguyên được gọi là vị từ trừu tượng. Trong cấu trúc bị chặn, hai nút u1, u2 được trộn với nhau nếu pS(u1) = pS(u2) cho mỗi vị từ trừu tượng p.

Khi các nút được trộn, các giá trị vị từ cho vị từ không trừu tượng của chúng sẽ được tham gia (tức là, kết quả là 1/2 nếu giá trị của chúng là khác

65

nhau). Các hoạt động của việc tính toán loại cấu trúc này được gọi là bị chặn mờ (Blur). Sự lựa chọn của các vị trừu tượng là rất quan trọng đối với sự cân bằng giữa không gian bộ nhớ và độ chính xác. TVLA cho phép người dùng lựa chọn các vị từ trừu tượng. Theo mặc định, tất cả các vị đơn nguyên là vị từ trừu tượng, như trong ví dụ trong phần 3.2.5.

Ví dụ trong hình 3.7, các nút u0 và u được phân biệt bởi thực tế là

( ) = 1, trong khi ( ) = 0 (tất cả vị từ khác là 0.) Nếu x không phải là một vị từ trừu tượng, thì các cấu trúc bị chặn thích hợp S’4 sẽ có một nút

duy nhất, khi đó ta nói rằng u với ( ) = 1/2 và ( , ) = 1/2 . 3.3.6. Thuật toán Coerce xác định hệ ràng buộc

Ràng buộc có tác dụng trong việc cải thiện độ chính xác của phân tích. Ràng buộc (còn gọi là quy tắc phi mâu thuẫn) là bất biến toàn cục trong

chương trình. Ví dụ, các ràng buộc trạng thái nếu nút u không được chia sẻ bởi trường n (is[n]S(u)=0) và không có một đường đi tới thông qua trường n. Vì vậy, nếu có tồn tại u1≠u2 như vậy thì nS(u1, u) = nS(u2, u) = 1 là cấu trúc

không đại diện cho bất kỳ cấu trúc cụ thể hợp lệ và có thể được loại bỏ.

∃ 1: (¬ [ ]( ) ∧ ( 1, ) ∧ 1 ≠ 2) ⊳ ¬ ( 2, )

Tương tự như vậy, nếu có tồn tại u1≠u2 sao cho n(u1, u)=1 và n(u2, u)=1/2, chúng ta có thể từng bước điều chỉnh n để n(u2, u) = 0. Ví dụ, trong hình 3.8, chúng ta sử dụng ràng buộc này trên cấu trúc So1 để loại bỏ vòng lặp

vô hạn trên chính u, và trên So2 để loại bỏ các vòng lặp vô hạn trên chính u.1 và cạnh vô hạn từ u.0 tới u.1.

Nói chung một ràng buộc được đưa ra với hình thức ⊳ , khi mà là một ký hiệu; gọi là thân của ràng buộc và được gọi là đầu của các

ràng buộc. Với một ràng buộc như vậy là hợp lý trên một cấu trúc S nếu cho

từng phép gán mà phần thân ( ) được đánh giá là 1 (không phải là 1/2), phần đầu cũng phải đánh giá là 1, ký hiệu: ⊨ 1 ⊳ 2. Ta nói rằng ràng buộc là vi phạm ký hiệu: ⊭ 1 ⊳ 2.

66

Định nghĩa 3.4: Cho cấu trúc S và một tập các ràng buộc XF, các thực thi

Coerce(S, XF) trả về cực đại S’, vì vậy ⊑ , = , và S’ không vi phạm

bất cứ ràng buộc trong XF. Coerce là không xác định nếu không tồn tại S’.

Định nghĩa 3.5: Đối với một mệnh đề Horn mở rộng φ, chúng ta xác định bao đóng của φ, biểu điễn bằng closure(φ) là tập các ràng buộc:

Bảng các ràng buộc sinh ra từ thuộc tính hàm thể hiện trong hình 3.7.

Đối với một vị từ đo p được định nghĩa bởi công thức φ các ràng buộc

sau đây được tạo ra: (i) ⊳ , (ii) ¬ ⊳ ¬ , và (iii) bao đóng của → , và

¬ → ¬ .

Một phần của tài liệu Nghiên cứu kỹ thuật giải thích trừu tượng (Trang 57)